-/** @file
-
-Copyright (c) 2004 - 2007, Intel Corporation
-All rights reserved. This program and the accompanying materials
-are licensed and made available under the terms and conditions of the BSD License
-which accompanies this distribution. The full text of the license may be found at
-http://opensource.org/licenses/bsd-license.php
-
-THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
-WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
-
-Module Name:
-
- ProcessOptions.c
-
-Abstract:
-
- Implementation for handling the User Interface option processing.
-
-Revision History
-
-
-**/
-
-#include "Ui.h"
-#include "Setup.h"
-
-
-/**
- Process Question Config.
-
- @param Selection The UI menu selection.
- @param Question The Question to be peocessed.
-
- @retval EFI_SUCCESS Question Config process success.
- @retval Other Question Config process fail.
-
-**/
-EFI_STATUS
-ProcessQuestionConfig (
- IN UI_MENU_SELECTION *Selection,
- IN FORM_BROWSER_STATEMENT *Question
- )
-{
- EFI_STATUS Status;
- CHAR16 *ConfigResp;
- CHAR16 *Progress;
- EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
-
- if (Question->QuestionConfig == 0) {
- return EFI_SUCCESS;
- }
-
- //
- // Get <ConfigResp>
- //
- ConfigResp = GetToken (Question->QuestionConfig, Selection->FormSet->HiiHandle);
- if (ConfigResp == NULL) {
- return EFI_NOT_FOUND;
- }
-
- //
- // Send config to Configuration Driver
- //
- ConfigAccess = Selection->FormSet->ConfigAccess;
- if (ConfigAccess == NULL) {
- return EFI_UNSUPPORTED;
- }
- Status = ConfigAccess->RouteConfig (
- ConfigAccess,
- ConfigResp,
- &Progress
- );
-
- return Status;
-}
-
-
-/**
- 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.
-
-**/
-QUESTION_OPTION *
-ValueToOption (
- IN FORM_BROWSER_STATEMENT *Question,
- IN EFI_HII_VALUE *OptionValue
- )
-{
- LIST_ENTRY *Link;
- QUESTION_OPTION *Option;
-
- Link = GetFirstNode (&Question->OptionListHead);
- while (!IsNull (&Question->OptionListHead, Link)) {
- Option = QUESTION_OPTION_FROM_LINK (Link);
-
- if (CompareHiiValue (&Option->Value, OptionValue, NULL) == 0) {
- return Option;
- }
-
- Link = GetNextNode (&Question->OptionListHead, Link);
- }
-
- return NULL;
-}
-
-
-/**
- Print Question Value according to it's storage width and display attributes.
-
- @param Event The event to wait for
- @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_BROWSER_STATEMENT *Question,
- IN OUT CHAR16 *FormattedNumber,
- IN UINTN BufferSize
- )
-{
- INT64 Value;
- CHAR16 *Format;
- EFI_HII_VALUE *QuestionValue;
-
- if (BufferSize < (21 * sizeof (CHAR16))) {
- return EFI_BUFFER_TOO_SMALL;
- }
-
- QuestionValue = &Question->HiiValue;
-
- Value = (INT64) QuestionValue->Value.u64;
- switch (Question->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;
-}
-
-
-/**
- Password may be stored as encrypted by Configuration Driver. When change a
- password, user will be challenged with old password. To validate user input old
- password, we will send the clear text to Configuration Driver via Callback().
- Configuration driver is responsible to check the passed in password and return
- the validation result. If validation pass, state machine in password Callback()
- will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.
- After user type in new password twice, Callback() will be invoked to send the
- new password to Configuration Driver.
-
- @param Selection Pointer to UI_MENU_SELECTION.
- @param MenuOption The MenuOption for this password Question.
- @param String The clear text of password.
-
- @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input.
- @return In state of BROWSER_STATE_VALIDATE_PASSWORD:
- @retval EFI_SUCCESS Password correct, Browser will prompt for new
- password.
- @retval EFI_NOT_READY Password incorrect, Browser will show error
- message.
- @retval Other Browser will do nothing.
- @return In state of BROWSER_STATE_SET_PASSWORD:
- @retval EFI_SUCCESS Set password success.
- @retval Other Set password failed.
-
-**/
-EFI_STATUS
-PasswordCallback (
- IN UI_MENU_SELECTION *Selection,
- IN UI_MENU_OPTION *MenuOption,
- IN CHAR16 *String
- )
-{
- EFI_STATUS Status;
- EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
- EFI_BROWSER_ACTION_REQUEST ActionRequest;
- EFI_HII_VALUE *QuestionValue;
-
- QuestionValue = &MenuOption->ThisTag->HiiValue;
- ConfigAccess = Selection->FormSet->ConfigAccess;
- if (ConfigAccess == NULL) {
- return EFI_UNSUPPORTED;
- }
-
- //
- // Prepare password string in HII database
- //
- if (String != NULL) {
- QuestionValue->Value.string = NewString (String, Selection->FormSet->HiiHandle);
- } else {
- QuestionValue->Value.string = 0;
- }
-
- //
- // Send password to Configuration Driver for validation
- //
- Status = ConfigAccess->Callback (
- ConfigAccess,
- EFI_BROWSER_ACTION_CHANGING,
- MenuOption->ThisTag->QuestionId,
- QuestionValue->Type,
- &QuestionValue->Value,
- &ActionRequest
- );
-
- //
- // Remove password string from HII database
- //
- if (String != NULL) {
- DeleteString (QuestionValue->Value.string, Selection->FormSet->HiiHandle);
- }
-
- return Status;
-}
-
-
-/**
- Display error message for invalid password.
-
- None.
-
- @return None.
-
-**/
-VOID
-PasswordInvalid (
- VOID
- )
-{
- EFI_INPUT_KEY Key;
-
- //
- // Invalid password, prompt error message
- //
- do {
- CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString);
- } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
-}
-
-
-/**
- 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;
-
- Status = EFI_SUCCESS;
-
- StringPtr = NULL;
- Character[1] = L'\0';
- *OptionString = NULL;
-
- ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
- BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow;
-
- Question = MenuOption->ThisTag;
- QuestionValue = &Question->HiiValue;
- Maximum = (UINT16) Question->Maximum;
-
- switch (Question->Operand) {
- case EFI_IFR_ORDERED_LIST_OP:
- //
- // Initialize Option value array
- //
- if (Question->BufferValue[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 = EFI_IFR_TYPE_NUM_SIZE_8;
- HiiValue.Value.u64 = 0;
- for (Index = 0; Index < Question->MaxContainers; Index++) {
- HiiValue.Value.u8 = Question->BufferValue[Index];
- if (HiiValue.Value.u8 == 0) {
- //
- // Values for the options in ordered lists should never be a 0
- //
- break;
- }
-
- OneOfOption = ValueToOption (Question, &HiiValue);
- if (OneOfOption == NULL) {
- gBS->FreePool (*OptionString);
- return EFI_NOT_FOUND;
- }
-
- Suppress = FALSE;
- if ((OneOfOption->SuppressExpression != NULL) &&
- (OneOfOption->SuppressExpression->Result.Value.b)) {
- //
- // This option is suppressed
- //
- Suppress = TRUE;
- }
-
- if (!Suppress) {
- Character[0] = LEFT_ONEOF_DELIMITER;
- NewStrCat (OptionString[0], Character);
- StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
- NewStrCat (OptionString[0], StringPtr);
- Character[0] = RIGHT_ONEOF_DELIMITER;
- NewStrCat (OptionString[0], Character);
- Character[0] = CHAR_CARRIAGE_RETURN;
- NewStrCat (OptionString[0], Character);
-
- gBS->FreePool (StringPtr);
- }
- }
- }
- break;
-
- case EFI_IFR_ONE_OF_OP:
- if (Selected) {
- //
- // Go ask for input
- //
- Status = GetSelectionInputPopUp (Selection, MenuOption);
- } else {
- *OptionString = AllocateZeroPool (BufferSize);
- ASSERT (*OptionString);
-
- OneOfOption = ValueToOption (Question, QuestionValue);
- if (OneOfOption == NULL) {
- gBS->FreePool (*OptionString);
- return EFI_NOT_FOUND;
- }
-
- if ((OneOfOption->SuppressExpression != NULL) &&
- (OneOfOption->SuppressExpression->Result.Value.b)) {
- //
- // 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) ||
- !OneOfOption->SuppressExpression->Result.Value.b) {
- Suppress = FALSE;
- CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE));
- SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
- break;
- }
-
- Link = GetNextNode (&Question->OptionListHead, Link);
- }
- }
-
- if (!Suppress) {
- Character[0] = LEFT_ONEOF_DELIMITER;
- NewStrCat (OptionString[0], Character);
- StringPtr = GetToken (OneOfOption->Text, Selection->Handle);
- NewStrCat (OptionString[0], StringPtr);
- Character[0] = RIGHT_ONEOF_DELIMITER;
- NewStrCat (OptionString[0], Character);
-
- gBS->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);
- gBS->FreePool (*OptionString);
- return Status;
- }
-
- //
- // Save Question value
- //
- Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
- UpdateStatusBar (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"%4d", 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);
-
- Status = ReadString (MenuOption, gPromptForData, StringPtr);
- if (!EFI_ERROR (Status)) {
- CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));
- SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
-
- UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
- }
-
- gBS->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) {
- //
- // Use a NULL password to test whether old password is required
- //
- *StringPtr = 0;
- Status = PasswordCallback (Selection, MenuOption, StringPtr);
- if (Status == EFI_NOT_AVAILABLE_YET) {
- //
- // Callback request to terminate password input
- //
- gBS->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)) {
- gBS->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;
- }
-
- gBS->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)) {
- gBS->FreePool (StringPtr);
- return Status;
- }
-
- TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue);
- TempString[Maximum] = L'\0';
-
- if (StrCmp (StringPtr, TempString) != 0) {
- //
- // Typed in old password incorrect
- //
- PasswordInvalid ();
-
- gBS->FreePool (StringPtr);
- gBS->FreePool (TempString);
- return Status;
- }
-
- gBS->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) {
- PasswordCallback (Selection, MenuOption, NULL);
- }
-
- gBS->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) {
- PasswordCallback (Selection, MenuOption, NULL);
- }
-
- gBS->FreePool (StringPtr);
- gBS->FreePool (TempString);
- return Status;
- }
-
- //
- // Compare two typed-in new passwords
- //
- if (StrCmp (StringPtr, TempString) == 0) {
- //
- // Two password match, send it to Configuration Driver
- //
- if (Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) {
- 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) {
- 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);
- }
-
- gBS->FreePool (TempString);
- gBS->FreePool (StringPtr);
- }
- 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 MenuOption The MenuOption for this Question.
- @param RowCount TRUE: if Question is selected.
- @param OptionString Pointer of the Option String to be displayed.
-
- @return None.
-
-**/
-VOID
-ProcessHelpString (
- IN CHAR16 *StringPtr,
- OUT CHAR16 **FormattedString,
- IN UINTN RowCount
- )
-{
- CONST UINTN BlockWidth = (UINTN) gHelpBlockWidth - 1;
- UINTN AllocateSize;
- //
- // [PrevCurrIndex, CurrIndex) forms a range of a screen-line
- //
- UINTN CurrIndex;
- UINTN PrevCurrIndex;
- UINTN LineCount;
- UINTN VirtualLineCount;
- //
- // GlyphOffset stores glyph width of current screen-line
- //
- UINTN GlyphOffset;
- //
- // GlyphWidth equals to 2 if we meet width directive
- //
- UINTN GlyphWidth;
- //
- // during scanning, we remember the position of last space character
- // in case that if next word cannot put in current line, we could restore back to the position
- // of last space character
- // while we should also remmeber the glyph width of the last space character for restoring
- //
- UINTN LastSpaceIndex;
- UINTN LastSpaceGlyphWidth;
- //
- // every time we begin to form a new screen-line, we should remember glyph width of single character
- // of last line
- //
- UINTN LineStartGlyphWidth;
- UINTN *IndexArray;
- UINTN *OldIndexArray;
-
- //
- // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )
- // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want
- // to bring the width directive of the last line to current screen-line.
- // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line
- // different from that of "\wideabcde", we should remember the width directive.
- //
- AllocateSize = 0x20;
- IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
-
- if (*FormattedString != NULL) {
- gBS->FreePool (*FormattedString);
- *FormattedString = NULL;
- }
-
- for (PrevCurrIndex = 0, CurrIndex = 0, LineCount = 0, LastSpaceIndex = 0,
- IndexArray[0] = 0, GlyphWidth = 1, GlyphOffset = 0, LastSpaceGlyphWidth = 1, LineStartGlyphWidth = 1;
- (StringPtr[CurrIndex] != CHAR_NULL);
- CurrIndex ++) {
-
- if (LineCount == AllocateSize) {
- AllocateSize += 0x10;
- OldIndexArray = IndexArray;
- IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);
- CopyMem (IndexArray, OldIndexArray, LineCount * sizeof (UINTN) * 3);
- gBS->FreePool (OldIndexArray);
- }
- switch (StringPtr[CurrIndex]) {
-
- case NARROW_CHAR:
- case WIDE_CHAR:
- GlyphWidth = ((StringPtr[CurrIndex] == WIDE_CHAR) ? 2 : 1);
- if (CurrIndex == 0) {
- LineStartGlyphWidth = GlyphWidth;
- }
- break;
-
- //
- // char is '\n'
- // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN
- //
- case CHAR_LINEFEED:
- //
- // Store a range of string as a line
- //
- IndexArray[LineCount*3] = PrevCurrIndex;
- IndexArray[LineCount*3+1] = CurrIndex;
- IndexArray[LineCount*3+2] = LineStartGlyphWidth;
- LineCount ++;
- //
- // Reset offset and save begin position of line
- //
- GlyphOffset = 0;
- LineStartGlyphWidth = GlyphWidth;
- PrevCurrIndex = CurrIndex + 1;
- break;
-
- //
- // char is '\r'
- // "\r\n" and "\r" both are handled here
- //
- case CHAR_CARRIAGE_RETURN:
- if (StringPtr[CurrIndex + 1] == CHAR_LINEFEED) {
- //
- // next char is '\n'
- //
- IndexArray[LineCount*3] = PrevCurrIndex;
- IndexArray[LineCount*3+1] = CurrIndex;
- IndexArray[LineCount*3+2] = LineStartGlyphWidth;
- LineCount ++;
- CurrIndex ++;
- }
- GlyphOffset = 0;
- LineStartGlyphWidth = GlyphWidth;
- PrevCurrIndex = CurrIndex + 1;
- break;
-
- //
- // char is space or other char
- //
- default:
- GlyphOffset += GlyphWidth;
- if (GlyphOffset >= BlockWidth) {
- if (LastSpaceIndex > PrevCurrIndex) {
- //
- // LastSpaceIndex points to space inside current screen-line,
- // restore to LastSpaceIndex
- // (Otherwise the word is too long to fit one screen-line, just cut it)
- //
- CurrIndex = LastSpaceIndex;
- GlyphWidth = LastSpaceGlyphWidth;
- } else if (GlyphOffset > BlockWidth) {
- //
- // the word is too long to fit one screen-line and we don't get the chance
- // of GlyphOffset == BlockWidth because GlyphWidth = 2
- //
- CurrIndex --;
- }
-
- IndexArray[LineCount*3] = PrevCurrIndex;
- IndexArray[LineCount*3+1] = CurrIndex + 1;
- IndexArray[LineCount*3+2] = LineStartGlyphWidth;
- LineStartGlyphWidth = GlyphWidth;
- LineCount ++;
- //
- // Reset offset and save begin position of line
- //
- GlyphOffset = 0;
- PrevCurrIndex = CurrIndex + 1;
- }
-
- //
- // LastSpaceIndex: remember position of last space
- //
- if (StringPtr[CurrIndex] == CHAR_SPACE) {
- LastSpaceIndex = CurrIndex;
- LastSpaceGlyphWidth = GlyphWidth;
- }
- break;
- }
- }
-
- if (GlyphOffset > 0) {
- IndexArray[LineCount*3] = PrevCurrIndex;
- IndexArray[LineCount*3+1] = CurrIndex;
- IndexArray[LineCount*3+2] = GlyphWidth;
- LineCount ++;
- }
-
- if (LineCount == 0) {
- //
- // in case we meet null string
- //
- IndexArray[0] = 0;
- IndexArray[1] = 1;
- //
- // we assume null string's glyph width is 1
- //
- IndexArray[1] = 1;
- LineCount ++;
- }
-
- VirtualLineCount = RowCount * (LineCount / RowCount + (LineCount % RowCount > 0));
- *FormattedString = AllocateZeroPool (VirtualLineCount * (BlockWidth + 1) * sizeof (CHAR16) * 2);
-
- for (CurrIndex = 0; CurrIndex < LineCount; CurrIndex ++) {
- *(*FormattedString + CurrIndex * 2 * (BlockWidth + 1)) = (CHAR16) ((IndexArray[CurrIndex*3+2] == 2) ? WIDE_CHAR : NARROW_CHAR);
- StrnCpy (
- *FormattedString + CurrIndex * 2 * (BlockWidth + 1) + 1,
- StringPtr + IndexArray[CurrIndex*3],
- IndexArray[CurrIndex*3+1]-IndexArray[CurrIndex*3]
- );
- }
-
- gBS->FreePool (IndexArray);
-}
+/** @file\r
+Implementation for handling the User Interface option processing.\r
+\r
+\r
+Copyright (c) 2004 - 2010, Intel Corporation. All rights reserved.<BR>\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#include "Setup.h"\r
+\r
+\r
+/**\r
+ Process Question Config.\r
+\r
+ @param Selection The UI menu selection.\r
+ @param Question The Question to be peocessed.\r
+\r
+ @retval EFI_SUCCESS Question Config process success.\r
+ @retval Other Question Config process fail.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessQuestionConfig (\r
+ IN UI_MENU_SELECTION *Selection,\r
+ IN FORM_BROWSER_STATEMENT *Question\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *ConfigResp;\r
+ CHAR16 *Progress;\r
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;\r
+\r
+ if (Question->QuestionConfig == 0) {\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ //\r
+ // Get <ConfigResp>\r
+ //\r
+ ConfigResp = GetToken (Question->QuestionConfig, Selection->FormSet->HiiHandle);\r
+ if (ConfigResp == NULL) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ //\r
+ // Send config to Configuration Driver\r
+ //\r
+ ConfigAccess = Selection->FormSet->ConfigAccess;\r
+ if (ConfigAccess == NULL) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+ Status = ConfigAccess->RouteConfig (\r
+ ConfigAccess,\r
+ ConfigResp,\r
+ &Progress\r
+ );\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Search an Option of a Question by its value.\r
+\r
+ @param Question The Question\r
+ @param OptionValue Value for Option to be searched.\r
+\r
+ @retval Pointer Pointer to the found Option.\r
+ @retval NULL Option not found.\r
+\r
+**/\r
+QUESTION_OPTION *\r
+ValueToOption (\r
+ IN FORM_BROWSER_STATEMENT *Question,\r
+ IN EFI_HII_VALUE *OptionValue\r
+ )\r
+{\r
+ LIST_ENTRY *Link;\r
+ QUESTION_OPTION *Option;\r
+\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ while (!IsNull (&Question->OptionListHead, Link)) {\r
+ Option = QUESTION_OPTION_FROM_LINK (Link);\r
+\r
+ if (CompareHiiValue (&Option->Value, OptionValue, NULL) == 0) {\r
+ return Option;\r
+ }\r
+\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+\r
+ return NULL;\r
+}\r
+\r
+\r
+/**\r
+ Return data element in an Array by its Index.\r
+\r
+ @param Array The data array.\r
+ @param Type Type of the data in this array.\r
+ @param Index Zero based index for data in this array.\r
+\r
+ @retval Value The data to be returned\r
+\r
+**/\r
+UINT64\r
+GetArrayData (\r
+ IN VOID *Array,\r
+ IN UINT8 Type,\r
+ IN UINTN Index\r
+ )\r
+{\r
+ UINT64 Data;\r
+\r
+ ASSERT (Array != NULL);\r
+\r
+ Data = 0;\r
+ switch (Type) {\r
+ case EFI_IFR_TYPE_NUM_SIZE_8:\r
+ Data = (UINT64) *(((UINT8 *) Array) + Index);\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_16:\r
+ Data = (UINT64) *(((UINT16 *) Array) + Index);\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_32:\r
+ Data = (UINT64) *(((UINT32 *) Array) + Index);\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_64:\r
+ Data = (UINT64) *(((UINT64 *) Array) + Index);\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return Data;\r
+}\r
+\r
+\r
+/**\r
+ Set value of a data element in an Array by its Index.\r
+\r
+ @param Array The data array.\r
+ @param Type Type of the data in this array.\r
+ @param Index Zero based index for data in this array.\r
+ @param Value The value to be set.\r
+\r
+**/\r
+VOID\r
+SetArrayData (\r
+ IN VOID *Array,\r
+ IN UINT8 Type,\r
+ IN UINTN Index,\r
+ IN UINT64 Value\r
+ )\r
+{\r
+\r
+ ASSERT (Array != NULL);\r
+\r
+ switch (Type) {\r
+ case EFI_IFR_TYPE_NUM_SIZE_8:\r
+ *(((UINT8 *) Array) + Index) = (UINT8) Value;\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_16:\r
+ *(((UINT16 *) Array) + Index) = (UINT16) Value;\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_32:\r
+ *(((UINT32 *) Array) + Index) = (UINT32) Value;\r
+ break;\r
+\r
+ case EFI_IFR_TYPE_NUM_SIZE_64:\r
+ *(((UINT64 *) Array) + Index) = (UINT64) Value;\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+ Print Question Value according to it's storage width and display attributes.\r
+\r
+ @param Question The Question to be printed.\r
+ @param FormattedNumber Buffer for output string.\r
+ @param BufferSize The FormattedNumber buffer size in bytes.\r
+\r
+ @retval EFI_SUCCESS Print success.\r
+ @retval EFI_BUFFER_TOO_SMALL Buffer size is not enough for formatted number.\r
+\r
+**/\r
+EFI_STATUS\r
+PrintFormattedNumber (\r
+ IN FORM_BROWSER_STATEMENT *Question,\r
+ IN OUT CHAR16 *FormattedNumber,\r
+ IN UINTN BufferSize\r
+ )\r
+{\r
+ INT64 Value;\r
+ CHAR16 *Format;\r
+ EFI_HII_VALUE *QuestionValue;\r
+\r
+ if (BufferSize < (21 * sizeof (CHAR16))) {\r
+ return EFI_BUFFER_TOO_SMALL;\r
+ }\r
+\r
+ QuestionValue = &Question->HiiValue;\r
+\r
+ Value = (INT64) QuestionValue->Value.u64;\r
+ switch (Question->Flags & EFI_IFR_DISPLAY) {\r
+ case EFI_IFR_DISPLAY_INT_DEC:\r
+ switch (QuestionValue->Type) {\r
+ case EFI_IFR_NUMERIC_SIZE_1:\r
+ Value = (INT64) ((INT8) QuestionValue->Value.u8);\r
+ break;\r
+\r
+ case EFI_IFR_NUMERIC_SIZE_2:\r
+ Value = (INT64) ((INT16) QuestionValue->Value.u16);\r
+ break;\r
+\r
+ case EFI_IFR_NUMERIC_SIZE_4:\r
+ Value = (INT64) ((INT32) QuestionValue->Value.u32);\r
+ break;\r
+\r
+ case EFI_IFR_NUMERIC_SIZE_8:\r
+ default:\r
+ break;\r
+ }\r
+\r
+ if (Value < 0) {\r
+ Value = -Value;\r
+ Format = L"-%ld";\r
+ } else {\r
+ Format = L"%ld";\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_DISPLAY_UINT_DEC:\r
+ Format = L"%ld";\r
+ break;\r
+\r
+ case EFI_IFR_DISPLAY_UINT_HEX:\r
+ Format = L"%lx";\r
+ break;\r
+\r
+ default:\r
+ return EFI_UNSUPPORTED;\r
+ break;\r
+ }\r
+\r
+ UnicodeSPrint (FormattedNumber, BufferSize, Format, Value);\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+\r
+/**\r
+ Password may be stored as encrypted by Configuration Driver. When change a\r
+ password, user will be challenged with old password. To validate user input old\r
+ password, we will send the clear text to Configuration Driver via Callback().\r
+ Configuration driver is responsible to check the passed in password and return\r
+ the validation result. If validation pass, state machine in password Callback()\r
+ will transit from BROWSER_STATE_VALIDATE_PASSWORD to BROWSER_STATE_SET_PASSWORD.\r
+ After user type in new password twice, Callback() will be invoked to send the\r
+ new password to Configuration Driver.\r
+\r
+ @param Selection Pointer to UI_MENU_SELECTION.\r
+ @param MenuOption The MenuOption for this password Question.\r
+ @param String The clear text of password.\r
+\r
+ @retval EFI_NOT_AVAILABLE_YET Callback() request to terminate password input.\r
+ @return In state of BROWSER_STATE_VALIDATE_PASSWORD:\r
+ @retval EFI_SUCCESS Password correct, Browser will prompt for new\r
+ password.\r
+ @retval EFI_NOT_READY Password incorrect, Browser will show error\r
+ message.\r
+ @retval Other Browser will do nothing.\r
+ @return In state of BROWSER_STATE_SET_PASSWORD:\r
+ @retval EFI_SUCCESS Set password success.\r
+ @retval Other Set password failed.\r
+\r
+**/\r
+EFI_STATUS\r
+PasswordCallback (\r
+ IN UI_MENU_SELECTION *Selection,\r
+ IN UI_MENU_OPTION *MenuOption,\r
+ IN CHAR16 *String\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;\r
+ EFI_BROWSER_ACTION_REQUEST ActionRequest;\r
+ EFI_HII_VALUE *QuestionValue;\r
+\r
+ QuestionValue = &MenuOption->ThisTag->HiiValue;\r
+ ConfigAccess = Selection->FormSet->ConfigAccess;\r
+ if (ConfigAccess == NULL) {\r
+ return EFI_UNSUPPORTED;\r
+ }\r
+\r
+ //\r
+ // Prepare password string in HII database\r
+ //\r
+ if (String != NULL) {\r
+ QuestionValue->Value.string = NewString (String, Selection->FormSet->HiiHandle);\r
+ } else {\r
+ QuestionValue->Value.string = 0;\r
+ }\r
+\r
+ //\r
+ // Send password to Configuration Driver for validation\r
+ //\r
+ Status = ConfigAccess->Callback (\r
+ ConfigAccess,\r
+ EFI_BROWSER_ACTION_CHANGING,\r
+ MenuOption->ThisTag->QuestionId,\r
+ QuestionValue->Type,\r
+ &QuestionValue->Value,\r
+ &ActionRequest\r
+ );\r
+\r
+ //\r
+ // Remove password string from HII database\r
+ //\r
+ if (String != NULL) {\r
+ DeleteString (QuestionValue->Value.string, Selection->FormSet->HiiHandle);\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Display error message for invalid password.\r
+\r
+**/\r
+VOID\r
+PasswordInvalid (\r
+ VOID\r
+ )\r
+{\r
+ EFI_INPUT_KEY Key;\r
+\r
+ //\r
+ // Invalid password, prompt error message\r
+ //\r
+ do {\r
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gPassowordInvalid, gPressEnter, gEmptyString);\r
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+}\r
+\r
+\r
+/**\r
+ Process a Question's Option (whether selected or un-selected).\r
+\r
+ @param Selection Pointer to UI_MENU_SELECTION.\r
+ @param MenuOption The MenuOption for this Question.\r
+ @param Selected TRUE: if Question is selected.\r
+ @param OptionString Pointer of the Option String to be displayed.\r
+\r
+ @retval EFI_SUCCESS Question Option process success.\r
+ @retval Other Question Option process fail.\r
+\r
+**/\r
+EFI_STATUS\r
+ProcessOptions (\r
+ IN UI_MENU_SELECTION *Selection,\r
+ IN UI_MENU_OPTION *MenuOption,\r
+ IN BOOLEAN Selected,\r
+ OUT CHAR16 **OptionString\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ CHAR16 *StringPtr;\r
+ CHAR16 *TempString;\r
+ UINTN Index;\r
+ FORM_BROWSER_STATEMENT *Question;\r
+ CHAR16 FormattedNumber[21];\r
+ UINT16 Number;\r
+ CHAR16 Character[2];\r
+ EFI_INPUT_KEY Key;\r
+ UINTN BufferSize;\r
+ QUESTION_OPTION *OneOfOption;\r
+ LIST_ENTRY *Link;\r
+ EFI_HII_VALUE HiiValue;\r
+ EFI_HII_VALUE *QuestionValue;\r
+ BOOLEAN Suppress;\r
+ UINT16 Maximum;\r
+ QUESTION_OPTION *Option;\r
+ UINTN Index2;\r
+ UINT8 *ValueArray;\r
+ UINT8 ValueType;\r
+\r
+ Status = EFI_SUCCESS;\r
+\r
+ StringPtr = NULL;\r
+ Character[1] = L'\0';\r
+ *OptionString = NULL;\r
+\r
+ ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
+ BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow;\r
+\r
+ Question = MenuOption->ThisTag;\r
+ QuestionValue = &Question->HiiValue;\r
+ Maximum = (UINT16) Question->Maximum;\r
+\r
+ ValueArray = Question->BufferValue;\r
+ ValueType = Question->ValueType;\r
+\r
+ switch (Question->Operand) {\r
+ case EFI_IFR_ORDERED_LIST_OP:\r
+ //\r
+ // Check whether there are Options of this OrderedList\r
+ //\r
+ if (IsListEmpty (&Question->OptionListHead)) {\r
+ break;\r
+ }\r
+ //\r
+ // Initialize Option value array\r
+ //\r
+ if (GetArrayData (ValueArray, ValueType, 0) == 0) {\r
+ GetQuestionDefault (Selection->FormSet, Selection->Form, Question, 0);\r
+ }\r
+\r
+ if (Selected) {\r
+ //\r
+ // Go ask for input\r
+ //\r
+ Status = GetSelectionInputPopUp (Selection, MenuOption);\r
+ } else {\r
+ //\r
+ // We now know how many strings we will have, so we can allocate the\r
+ // space required for the array or strings.\r
+ //\r
+ *OptionString = AllocateZeroPool (Question->MaxContainers * BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ HiiValue.Type = ValueType;\r
+ HiiValue.Value.u64 = 0;\r
+ for (Index = 0; Index < Question->MaxContainers; Index++) {\r
+ HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
+ if (HiiValue.Value.u64 == 0) {\r
+ //\r
+ // Values for the options in ordered lists should never be a 0\r
+ //\r
+ break;\r
+ }\r
+\r
+ OneOfOption = ValueToOption (Question, &HiiValue);\r
+ if (OneOfOption == NULL) {\r
+ //\r
+ // Show error message\r
+ //\r
+ do {\r
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);\r
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+\r
+ //\r
+ // The initial value of the orderedlist is invalid, force to be valid value\r
+ //\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ Index2 = 0;\r
+ while (!IsNull (&Question->OptionListHead, Link) && Index2 < Question->MaxContainers) {\r
+ Option = QUESTION_OPTION_FROM_LINK (Link);\r
+ SetArrayData (ValueArray, ValueType, Index2, Option->Value.Value.u64);\r
+ Index2++;\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+ SetArrayData (ValueArray, ValueType, Index2, 0);\r
+\r
+ Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+\r
+ FreePool (*OptionString);\r
+ *OptionString = NULL;\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ Suppress = FALSE;\r
+ if ((OneOfOption->SuppressExpression != NULL) &&\r
+ (OneOfOption->SuppressExpression->Result.Value.b)) {\r
+ //\r
+ // This option is suppressed\r
+ //\r
+ Suppress = TRUE;\r
+ }\r
+\r
+ if (!Suppress) {\r
+ Character[0] = LEFT_ONEOF_DELIMITER;\r
+ NewStrCat (OptionString[0], Character);\r
+ StringPtr = GetToken (OneOfOption->Text, Selection->Handle);\r
+ NewStrCat (OptionString[0], StringPtr);\r
+ Character[0] = RIGHT_ONEOF_DELIMITER;\r
+ NewStrCat (OptionString[0], Character);\r
+ Character[0] = CHAR_CARRIAGE_RETURN;\r
+ NewStrCat (OptionString[0], Character);\r
+\r
+ FreePool (StringPtr);\r
+ }\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_ONE_OF_OP:\r
+ //\r
+ // Check whether there are Options of this OneOf\r
+ //\r
+ if (IsListEmpty (&Question->OptionListHead)) {\r
+ break;\r
+ }\r
+ if (Selected) {\r
+ //\r
+ // Go ask for input\r
+ //\r
+ Status = GetSelectionInputPopUp (Selection, MenuOption);\r
+ } else {\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ OneOfOption = ValueToOption (Question, QuestionValue);\r
+ if (OneOfOption == NULL) {\r
+ //\r
+ // Show error message\r
+ //\r
+ do {\r
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString);\r
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+\r
+ //\r
+ // Force the Question value to be valid\r
+ //\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ while (!IsNull (&Question->OptionListHead, Link)) {\r
+ Option = QUESTION_OPTION_FROM_LINK (Link);\r
+\r
+ if ((Option->SuppressExpression == NULL) ||\r
+ !Option->SuppressExpression->Result.Value.b) {\r
+ CopyMem (QuestionValue, &Option->Value, sizeof (EFI_HII_VALUE));\r
+ SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ break;\r
+ }\r
+\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+\r
+ FreePool (*OptionString);\r
+ *OptionString = NULL;\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ if ((OneOfOption->SuppressExpression != NULL) &&\r
+ (OneOfOption->SuppressExpression->Result.Value.b)) {\r
+ //\r
+ // This option is suppressed\r
+ //\r
+ Suppress = TRUE;\r
+ } else {\r
+ Suppress = FALSE;\r
+ }\r
+\r
+ if (Suppress) {\r
+ //\r
+ // Current selected option happen to be suppressed,\r
+ // enforce to select on a non-suppressed option\r
+ //\r
+ Link = GetFirstNode (&Question->OptionListHead);\r
+ while (!IsNull (&Question->OptionListHead, Link)) {\r
+ OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
+\r
+ if ((OneOfOption->SuppressExpression == NULL) ||\r
+ !OneOfOption->SuppressExpression->Result.Value.b) {\r
+ Suppress = FALSE;\r
+ CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE));\r
+ SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);\r
+ break;\r
+ }\r
+\r
+ Link = GetNextNode (&Question->OptionListHead, Link);\r
+ }\r
+ }\r
+\r
+ if (!Suppress) {\r
+ Character[0] = LEFT_ONEOF_DELIMITER;\r
+ NewStrCat (OptionString[0], Character);\r
+ StringPtr = GetToken (OneOfOption->Text, Selection->Handle);\r
+ NewStrCat (OptionString[0], StringPtr);\r
+ Character[0] = RIGHT_ONEOF_DELIMITER;\r
+ NewStrCat (OptionString[0], Character);\r
+\r
+ FreePool (StringPtr);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_CHECKBOX_OP:\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ *OptionString[0] = LEFT_CHECKBOX_DELIMITER;\r
+\r
+ if (Selected) {\r
+ //\r
+ // Since this is a BOOLEAN operation, flip it upon selection\r
+ //\r
+ QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);\r
+\r
+ //\r
+ // Perform inconsistent check\r
+ //\r
+ Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Inconsistent check fail, restore Question Value\r
+ //\r
+ QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE);\r
+ FreePool (*OptionString);\r
+ *OptionString = NULL;\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Save Question value\r
+ //\r
+ Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ }\r
+\r
+ if (QuestionValue->Value.b) {\r
+ *(OptionString[0] + 1) = CHECK_ON;\r
+ } else {\r
+ *(OptionString[0] + 1) = CHECK_OFF;\r
+ }\r
+ *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER;\r
+ break;\r
+\r
+ case EFI_IFR_NUMERIC_OP:\r
+ if (Selected) {\r
+ //\r
+ // Go ask for input\r
+ //\r
+ Status = GetNumericInput (Selection, MenuOption);\r
+ } else {\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ *OptionString[0] = LEFT_NUMERIC_DELIMITER;\r
+\r
+ //\r
+ // Formatted print\r
+ //\r
+ PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
+ Number = (UINT16) GetStringWidth (FormattedNumber);\r
+ CopyMem (OptionString[0] + 1, FormattedNumber, Number);\r
+\r
+ *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER;\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_DATE_OP:\r
+ if (Selected) {\r
+ //\r
+ // This is similar to numerics\r
+ //\r
+ Status = GetNumericInput (Selection, MenuOption);\r
+ } else {\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ *OptionString[0] = LEFT_NUMERIC_DELIMITER;\r
+ UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month);\r
+ *(OptionString[0] + 3) = DATE_SEPARATOR;\r
+ break;\r
+\r
+ case 1:\r
+ SetUnicodeMem (OptionString[0], 4, L' ');\r
+ UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day);\r
+ *(OptionString[0] + 6) = DATE_SEPARATOR;\r
+ break;\r
+\r
+ case 2:\r
+ SetUnicodeMem (OptionString[0], 7, L' ');\r
+ UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%4d", QuestionValue->Value.date.Year);\r
+ *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER;\r
+ break;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_TIME_OP:\r
+ if (Selected) {\r
+ //\r
+ // This is similar to numerics\r
+ //\r
+ Status = GetNumericInput (Selection, MenuOption);\r
+ } else {\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ switch (MenuOption->Sequence) {\r
+ case 0:\r
+ *OptionString[0] = LEFT_NUMERIC_DELIMITER;\r
+ UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour);\r
+ *(OptionString[0] + 3) = TIME_SEPARATOR;\r
+ break;\r
+\r
+ case 1:\r
+ SetUnicodeMem (OptionString[0], 4, L' ');\r
+ UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute);\r
+ *(OptionString[0] + 6) = TIME_SEPARATOR;\r
+ break;\r
+\r
+ case 2:\r
+ SetUnicodeMem (OptionString[0], 7, L' ');\r
+ UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second);\r
+ *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER;\r
+ break;\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_STRING_OP:\r
+ if (Selected) {\r
+ StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));\r
+ ASSERT (StringPtr);\r
+\r
+ Status = ReadString (MenuOption, gPromptForData, StringPtr);\r
+ if (!EFI_ERROR (Status)) {\r
+ CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));\r
+ SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
+\r
+ UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
+ }\r
+\r
+ FreePool (StringPtr);\r
+ } else {\r
+ *OptionString = AllocateZeroPool (BufferSize);\r
+ ASSERT (*OptionString);\r
+\r
+ if (((CHAR16 *) Question->BufferValue)[0] == 0x0000) {\r
+ *(OptionString[0]) = '_';\r
+ } else {\r
+ if ((Maximum * sizeof (CHAR16)) < BufferSize) {\r
+ BufferSize = Maximum * sizeof (CHAR16);\r
+ }\r
+ CopyMem (OptionString[0], (CHAR16 *) Question->BufferValue, BufferSize);\r
+ }\r
+ }\r
+ break;\r
+\r
+ case EFI_IFR_PASSWORD_OP:\r
+ if (Selected) {\r
+ StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));\r
+ ASSERT (StringPtr);\r
+\r
+ //\r
+ // For interactive passwords, old password is validated by callback\r
+ //\r
+ if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {\r
+ //\r
+ // Use a NULL password to test whether old password is required\r
+ //\r
+ *StringPtr = 0;\r
+ Status = PasswordCallback (Selection, MenuOption, StringPtr);\r
+ if (Status == EFI_NOT_AVAILABLE_YET) {\r
+ //\r
+ // Callback request to terminate password input\r
+ //\r
+ FreePool (StringPtr);\r
+ return EFI_SUCCESS;\r
+ }\r
+\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Old password exist, ask user for the old password\r
+ //\r
+ Status = ReadString (MenuOption, gPromptForPassword, StringPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (StringPtr);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Check user input old password\r
+ //\r
+ Status = PasswordCallback (Selection, MenuOption, StringPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ if (Status == EFI_NOT_READY) {\r
+ //\r
+ // Typed in old password incorrect\r
+ //\r
+ PasswordInvalid ();\r
+ } else {\r
+ Status = EFI_SUCCESS;\r
+ }\r
+\r
+ FreePool (StringPtr);\r
+ return Status;\r
+ }\r
+ }\r
+ } else {\r
+ //\r
+ // For non-interactive password, validate old password in local\r
+ //\r
+ if (*((CHAR16 *) Question->BufferValue) != 0) {\r
+ //\r
+ // There is something there! Prompt for password\r
+ //\r
+ Status = ReadString (MenuOption, gPromptForPassword, StringPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ FreePool (StringPtr);\r
+ return Status;\r
+ }\r
+\r
+ TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue);\r
+ ASSERT (TempString != NULL);\r
+\r
+ TempString[Maximum] = L'\0';\r
+\r
+ if (StrCmp (StringPtr, TempString) != 0) {\r
+ //\r
+ // Typed in old password incorrect\r
+ //\r
+ PasswordInvalid ();\r
+\r
+ FreePool (StringPtr);\r
+ FreePool (TempString);\r
+ return Status;\r
+ }\r
+\r
+ FreePool (TempString);\r
+ }\r
+ }\r
+\r
+ //\r
+ // Ask for new password\r
+ //\r
+ ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16));\r
+ Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Reset state machine for interactive password\r
+ //\r
+ if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {\r
+ PasswordCallback (Selection, MenuOption, NULL);\r
+ }\r
+\r
+ FreePool (StringPtr);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Confirm new password\r
+ //\r
+ TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16));\r
+ ASSERT (TempString);\r
+ Status = ReadString (MenuOption, gConfirmPassword, TempString);\r
+ if (EFI_ERROR (Status)) {\r
+ //\r
+ // Reset state machine for interactive password\r
+ //\r
+ if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {\r
+ PasswordCallback (Selection, MenuOption, NULL);\r
+ }\r
+\r
+ FreePool (StringPtr);\r
+ FreePool (TempString);\r
+ return Status;\r
+ }\r
+\r
+ //\r
+ // Compare two typed-in new passwords\r
+ //\r
+ if (StrCmp (StringPtr, TempString) == 0) {\r
+ //\r
+ // Two password match, send it to Configuration Driver\r
+ //\r
+ if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {\r
+ PasswordCallback (Selection, MenuOption, StringPtr);\r
+ } else {\r
+ CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16));\r
+ SetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);\r
+ }\r
+ } else {\r
+ //\r
+ // Reset state machine for interactive password\r
+ //\r
+ if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {\r
+ PasswordCallback (Selection, MenuOption, NULL);\r
+ }\r
+\r
+ //\r
+ // Two password mismatch, prompt error message\r
+ //\r
+ do {\r
+ CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString);\r
+ } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
+ }\r
+\r
+ FreePool (TempString);\r
+ FreePool (StringPtr);\r
+ }\r
+ break;\r
+\r
+ default:\r
+ break;\r
+ }\r
+\r
+ return Status;\r
+}\r
+\r
+\r
+/**\r
+ Process the help string: Split StringPtr to several lines of strings stored in\r
+ FormattedString and the glyph width of each line cannot exceed gHelpBlockWidth.\r
+\r
+ @param StringPtr The entire help string.\r
+ @param FormattedString The oupput formatted string.\r
+ @param RowCount TRUE: if Question is selected.\r
+\r
+**/\r
+VOID\r
+ProcessHelpString (\r
+ IN CHAR16 *StringPtr,\r
+ OUT CHAR16 **FormattedString,\r
+ IN UINTN RowCount\r
+ )\r
+{\r
+ UINTN BlockWidth;\r
+ UINTN AllocateSize;\r
+ //\r
+ // [PrevCurrIndex, CurrIndex) forms a range of a screen-line\r
+ //\r
+ UINTN CurrIndex;\r
+ UINTN PrevCurrIndex;\r
+ UINTN LineCount;\r
+ UINTN VirtualLineCount;\r
+ //\r
+ // GlyphOffset stores glyph width of current screen-line\r
+ //\r
+ UINTN GlyphOffset;\r
+ //\r
+ // GlyphWidth equals to 2 if we meet width directive\r
+ //\r
+ UINTN GlyphWidth;\r
+ //\r
+ // during scanning, we remember the position of last space character\r
+ // in case that if next word cannot put in current line, we could restore back to the position\r
+ // of last space character\r
+ // while we should also remmeber the glyph width of the last space character for restoring\r
+ //\r
+ UINTN LastSpaceIndex;\r
+ UINTN LastSpaceGlyphWidth;\r
+ //\r
+ // every time we begin to form a new screen-line, we should remember glyph width of single character\r
+ // of last line\r
+ //\r
+ UINTN LineStartGlyphWidth;\r
+ UINTN *IndexArray;\r
+ UINTN *OldIndexArray;\r
+\r
+ BlockWidth = (UINTN) gHelpBlockWidth - 1;\r
+\r
+ //\r
+ // every three elements of IndexArray form a screen-line of string:[ IndexArray[i*3], IndexArray[i*3+1] )\r
+ // IndexArray[i*3+2] stores the initial glyph width of single character. to save this is because we want\r
+ // to bring the width directive of the last line to current screen-line.\r
+ // e.g.: "\wideabcde ... fghi", if "fghi" also has width directive but is splitted to the next screen-line\r
+ // different from that of "\wideabcde", we should remember the width directive.\r
+ //\r
+ AllocateSize = 0x20;\r
+ IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);\r
+ ASSERT (IndexArray != NULL);\r
+\r
+ if (*FormattedString != NULL) {\r
+ FreePool (*FormattedString);\r
+ *FormattedString = NULL;\r
+ }\r
+\r
+ for (PrevCurrIndex = 0, CurrIndex = 0, LineCount = 0, LastSpaceIndex = 0,\r
+ IndexArray[0] = 0, GlyphWidth = 1, GlyphOffset = 0, LastSpaceGlyphWidth = 1, LineStartGlyphWidth = 1;\r
+ (StringPtr[CurrIndex] != CHAR_NULL);\r
+ CurrIndex ++) {\r
+\r
+ if (LineCount == AllocateSize) {\r
+ AllocateSize += 0x10;\r
+ OldIndexArray = IndexArray;\r
+ IndexArray = AllocatePool (AllocateSize * sizeof (UINTN) * 3);\r
+ ASSERT (IndexArray != NULL);\r
+\r
+ CopyMem (IndexArray, OldIndexArray, LineCount * sizeof (UINTN) * 3);\r
+ FreePool (OldIndexArray);\r
+ }\r
+ switch (StringPtr[CurrIndex]) {\r
+\r
+ case NARROW_CHAR:\r
+ case WIDE_CHAR:\r
+ GlyphWidth = ((StringPtr[CurrIndex] == WIDE_CHAR) ? 2 : 1);\r
+ if (CurrIndex == 0) {\r
+ LineStartGlyphWidth = GlyphWidth;\r
+ }\r
+ break;\r
+\r
+ //\r
+ // char is '\n'\r
+ // "\r\n" isn't handled here, handled by case CHAR_CARRIAGE_RETURN\r
+ //\r
+ case CHAR_LINEFEED:\r
+ //\r
+ // Store a range of string as a line\r
+ //\r
+ IndexArray[LineCount*3] = PrevCurrIndex;\r
+ IndexArray[LineCount*3+1] = CurrIndex;\r
+ IndexArray[LineCount*3+2] = LineStartGlyphWidth;\r
+ LineCount ++;\r
+ //\r
+ // Reset offset and save begin position of line\r
+ //\r
+ GlyphOffset = 0;\r
+ LineStartGlyphWidth = GlyphWidth;\r
+ PrevCurrIndex = CurrIndex + 1;\r
+ break;\r
+\r
+ //\r
+ // char is '\r'\r
+ // "\r\n" and "\r" both are handled here\r
+ //\r
+ case CHAR_CARRIAGE_RETURN:\r
+ if (StringPtr[CurrIndex + 1] == CHAR_LINEFEED) {\r
+ //\r
+ // next char is '\n'\r
+ //\r
+ IndexArray[LineCount*3] = PrevCurrIndex;\r
+ IndexArray[LineCount*3+1] = CurrIndex;\r
+ IndexArray[LineCount*3+2] = LineStartGlyphWidth;\r
+ LineCount ++;\r
+ CurrIndex ++;\r
+ }\r
+ GlyphOffset = 0;\r
+ LineStartGlyphWidth = GlyphWidth;\r
+ PrevCurrIndex = CurrIndex + 1;\r
+ break;\r
+\r
+ //\r
+ // char is space or other char\r
+ //\r
+ default:\r
+ GlyphOffset += GlyphWidth;\r
+ if (GlyphOffset >= BlockWidth) {\r
+ if (LastSpaceIndex > PrevCurrIndex) {\r
+ //\r
+ // LastSpaceIndex points to space inside current screen-line,\r
+ // restore to LastSpaceIndex\r
+ // (Otherwise the word is too long to fit one screen-line, just cut it)\r
+ //\r
+ CurrIndex = LastSpaceIndex;\r
+ GlyphWidth = LastSpaceGlyphWidth;\r
+ } else if (GlyphOffset > BlockWidth) {\r
+ //\r
+ // the word is too long to fit one screen-line and we don't get the chance\r
+ // of GlyphOffset == BlockWidth because GlyphWidth = 2\r
+ //\r
+ CurrIndex --;\r
+ }\r
+\r
+ IndexArray[LineCount*3] = PrevCurrIndex;\r
+ IndexArray[LineCount*3+1] = CurrIndex + 1;\r
+ IndexArray[LineCount*3+2] = LineStartGlyphWidth;\r
+ LineStartGlyphWidth = GlyphWidth;\r
+ LineCount ++;\r
+ //\r
+ // Reset offset and save begin position of line\r
+ //\r
+ GlyphOffset = 0;\r
+ PrevCurrIndex = CurrIndex + 1;\r
+ }\r
+\r
+ //\r
+ // LastSpaceIndex: remember position of last space\r
+ //\r
+ if (StringPtr[CurrIndex] == CHAR_SPACE) {\r
+ LastSpaceIndex = CurrIndex;\r
+ LastSpaceGlyphWidth = GlyphWidth;\r
+ }\r
+ break;\r
+ }\r
+ }\r
+\r
+ if (GlyphOffset > 0) {\r
+ IndexArray[LineCount*3] = PrevCurrIndex;\r
+ IndexArray[LineCount*3+1] = CurrIndex;\r
+ IndexArray[LineCount*3+2] = GlyphWidth;\r
+ LineCount ++;\r
+ }\r
+\r
+ if (LineCount == 0) {\r
+ //\r
+ // in case we meet null string\r
+ //\r
+ IndexArray[0] = 0;\r
+ IndexArray[1] = 1;\r
+ //\r
+ // we assume null string's glyph width is 1\r
+ //\r
+ IndexArray[1] = 1;\r
+ LineCount ++;\r
+ }\r
+\r
+ VirtualLineCount = RowCount * (LineCount / RowCount + (LineCount % RowCount > 0));\r
+ *FormattedString = AllocateZeroPool (VirtualLineCount * (BlockWidth + 1) * sizeof (CHAR16) * 2);\r
+ ASSERT (*FormattedString != NULL);\r
+\r
+ for (CurrIndex = 0; CurrIndex < LineCount; CurrIndex ++) {\r
+ *(*FormattedString + CurrIndex * 2 * (BlockWidth + 1)) = (CHAR16) ((IndexArray[CurrIndex*3+2] == 2) ? WIDE_CHAR : NARROW_CHAR);\r
+ StrnCpy (\r
+ *FormattedString + CurrIndex * 2 * (BlockWidth + 1) + 1,\r
+ StringPtr + IndexArray[CurrIndex*3],\r
+ IndexArray[CurrIndex*3+1]-IndexArray[CurrIndex*3]\r
+ );\r
+ }\r
+\r
+ FreePool (IndexArray);\r
+}\r