mirror of https://github.com/acidanthera/audk.git
3590 lines
109 KiB
C
3590 lines
109 KiB
C
/** @file
|
|
Entry and initialization module for the browser.
|
|
|
|
Copyright (c) 2007 - 2014, Intel Corporation. All rights reserved.<BR>
|
|
Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<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"
|
|
|
|
//
|
|
// Search table for UiDisplayMenu()
|
|
//
|
|
SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
|
|
{
|
|
SCAN_UP,
|
|
UiUp,
|
|
},
|
|
{
|
|
SCAN_DOWN,
|
|
UiDown,
|
|
},
|
|
{
|
|
SCAN_PAGE_UP,
|
|
UiPageUp,
|
|
},
|
|
{
|
|
SCAN_PAGE_DOWN,
|
|
UiPageDown,
|
|
},
|
|
{
|
|
SCAN_ESC,
|
|
UiReset,
|
|
},
|
|
{
|
|
SCAN_LEFT,
|
|
UiLeft,
|
|
},
|
|
{
|
|
SCAN_RIGHT,
|
|
UiRight,
|
|
}
|
|
};
|
|
|
|
UINTN mScanCodeNumber = sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]);
|
|
|
|
SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
|
|
{
|
|
UiNoOperation,
|
|
CfUiNoOperation,
|
|
},
|
|
{
|
|
UiSelect,
|
|
CfUiSelect,
|
|
},
|
|
{
|
|
UiUp,
|
|
CfUiUp,
|
|
},
|
|
{
|
|
UiDown,
|
|
CfUiDown,
|
|
},
|
|
{
|
|
UiLeft,
|
|
CfUiLeft,
|
|
},
|
|
{
|
|
UiRight,
|
|
CfUiRight,
|
|
},
|
|
{
|
|
UiReset,
|
|
CfUiReset,
|
|
},
|
|
{
|
|
UiPageUp,
|
|
CfUiPageUp,
|
|
},
|
|
{
|
|
UiPageDown,
|
|
CfUiPageDown
|
|
},
|
|
{
|
|
UiHotKey,
|
|
CfUiHotKey
|
|
}
|
|
};
|
|
|
|
EFI_GUID gDisplayEngineGuid = {
|
|
0xE38C1029, 0xE38F, 0x45b9, {0x8F, 0x0D, 0xE2, 0xE6, 0x0B, 0xC9, 0xB2, 0x62}
|
|
};
|
|
|
|
FORM_ENTRY_INFO gFormEntryInfo;
|
|
UINTN gSequence;
|
|
EFI_SCREEN_DESCRIPTOR gStatementDimensions;
|
|
BOOLEAN mStatementLayoutIsChanged = TRUE;
|
|
USER_INPUT *gUserInput;
|
|
FORM_DISPLAY_ENGINE_FORM *gFormData;
|
|
EFI_HII_HANDLE gHiiHandle;
|
|
UINT16 gDirection;
|
|
LIST_ENTRY gMenuOption;
|
|
DISPLAY_HIGHLIGHT_MENU_INFO gHighligthMenuInfo = {0};
|
|
BOOLEAN mIsFirstForm = TRUE;
|
|
FORM_ENTRY_INFO gOldFormEntry = {0};
|
|
|
|
//
|
|
// Browser Global Strings
|
|
//
|
|
CHAR16 *gFormNotFound;
|
|
CHAR16 *gNoSubmitIf;
|
|
CHAR16 *gBrwoserError;
|
|
CHAR16 *gSaveFailed;
|
|
CHAR16 *gNoSubmitIfFailed;
|
|
CHAR16 *gSaveProcess;
|
|
CHAR16 *gSaveNoSubmitProcess;
|
|
CHAR16 *gDiscardChange;
|
|
CHAR16 *gJumpToFormSet;
|
|
CHAR16 *gCheckError;
|
|
CHAR16 *gPromptForData;
|
|
CHAR16 *gPromptForPassword;
|
|
CHAR16 *gPromptForNewPassword;
|
|
CHAR16 *gConfirmPassword;
|
|
CHAR16 *gConfirmError;
|
|
CHAR16 *gPassowordInvalid;
|
|
CHAR16 *gPressEnter;
|
|
CHAR16 *gEmptyString;
|
|
CHAR16 *gMiniString;
|
|
CHAR16 *gOptionMismatch;
|
|
CHAR16 *gFormSuppress;
|
|
CHAR16 *gProtocolNotFound;
|
|
|
|
CHAR16 gModalSkipColumn;
|
|
CHAR16 gPromptBlockWidth;
|
|
CHAR16 gOptionBlockWidth;
|
|
CHAR16 gHelpBlockWidth;
|
|
CHAR16 *mUnknownString;
|
|
|
|
FORM_DISPLAY_DRIVER_PRIVATE_DATA mPrivateData = {
|
|
FORM_DISPLAY_DRIVER_SIGNATURE,
|
|
NULL,
|
|
{
|
|
FormDisplay,
|
|
DriverClearDisplayPage,
|
|
ConfirmDataChange
|
|
}
|
|
};
|
|
|
|
|
|
/**
|
|
Get the string based on the StringId and HII Package List Handle.
|
|
|
|
@param Token The String's ID.
|
|
@param HiiHandle The package list in the HII database to search for
|
|
the specified string.
|
|
|
|
@return The output string.
|
|
|
|
**/
|
|
CHAR16 *
|
|
GetToken (
|
|
IN EFI_STRING_ID Token,
|
|
IN EFI_HII_HANDLE HiiHandle
|
|
)
|
|
{
|
|
EFI_STRING String;
|
|
|
|
String = HiiGetString (HiiHandle, Token, NULL);
|
|
if (String == NULL) {
|
|
String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString);
|
|
ASSERT (String != NULL);
|
|
}
|
|
|
|
return (CHAR16 *) String;
|
|
}
|
|
|
|
|
|
/**
|
|
Initialize the HII String Token to the correct values.
|
|
|
|
**/
|
|
VOID
|
|
InitializeDisplayStrings (
|
|
VOID
|
|
)
|
|
{
|
|
mUnknownString = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle);
|
|
gSaveFailed = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle);
|
|
gNoSubmitIfFailed = GetToken (STRING_TOKEN (NO_SUBMIT_IF_CHECK_FAILED), gHiiHandle);
|
|
gSaveProcess = GetToken (STRING_TOKEN (DISCARD_OR_JUMP), gHiiHandle);
|
|
gSaveNoSubmitProcess = GetToken (STRING_TOKEN (DISCARD_OR_CHECK), gHiiHandle);
|
|
gDiscardChange = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_DISCARD), gHiiHandle);
|
|
gJumpToFormSet = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_JUMP), gHiiHandle);
|
|
gCheckError = GetToken (STRING_TOKEN (DISCARD_OR_CHECK_CHECK), gHiiHandle);
|
|
gPromptForData = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
|
|
gPromptForPassword = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle);
|
|
gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle);
|
|
gConfirmPassword = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle);
|
|
gConfirmError = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle);
|
|
gPassowordInvalid = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle);
|
|
gPressEnter = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle);
|
|
gEmptyString = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
|
|
gMiniString = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle);
|
|
gOptionMismatch = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle);
|
|
gFormSuppress = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle);
|
|
gProtocolNotFound = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle);
|
|
gFormNotFound = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle);
|
|
gNoSubmitIf = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle);
|
|
gBrwoserError = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle);
|
|
}
|
|
|
|
/**
|
|
Free up the resource allocated for all strings required
|
|
by Setup Browser.
|
|
|
|
**/
|
|
VOID
|
|
FreeDisplayStrings (
|
|
VOID
|
|
)
|
|
{
|
|
FreePool (mUnknownString);
|
|
FreePool (gEmptyString);
|
|
FreePool (gSaveFailed);
|
|
FreePool (gNoSubmitIfFailed);
|
|
FreePool (gSaveProcess);
|
|
FreePool (gSaveNoSubmitProcess);
|
|
FreePool (gDiscardChange);
|
|
FreePool (gJumpToFormSet);
|
|
FreePool (gCheckError);
|
|
FreePool (gPromptForData);
|
|
FreePool (gPromptForPassword);
|
|
FreePool (gPromptForNewPassword);
|
|
FreePool (gConfirmPassword);
|
|
FreePool (gConfirmError);
|
|
FreePool (gPassowordInvalid);
|
|
FreePool (gPressEnter);
|
|
FreePool (gMiniString);
|
|
FreePool (gOptionMismatch);
|
|
FreePool (gFormSuppress);
|
|
FreePool (gProtocolNotFound);
|
|
FreePool (gBrwoserError);
|
|
FreePool (gNoSubmitIf);
|
|
FreePool (gFormNotFound);
|
|
}
|
|
|
|
/**
|
|
Get prompt string id from the opcode data buffer.
|
|
|
|
@param OpCode The input opcode buffer.
|
|
|
|
@return The prompt string id.
|
|
|
|
**/
|
|
EFI_STRING_ID
|
|
GetPrompt (
|
|
IN EFI_IFR_OP_HEADER *OpCode
|
|
)
|
|
{
|
|
EFI_IFR_STATEMENT_HEADER *Header;
|
|
|
|
if (OpCode->Length <= sizeof (EFI_IFR_OP_HEADER)) {
|
|
return 0;
|
|
}
|
|
|
|
Header = (EFI_IFR_STATEMENT_HEADER *) (OpCode + 1);
|
|
|
|
return Header->Prompt;
|
|
}
|
|
|
|
/**
|
|
Get the supported width for a particular op-code
|
|
|
|
@param MenuOption The menu option.
|
|
@param AdjustWidth The width which is saved for the space.
|
|
|
|
@return Returns the number of CHAR16 characters that is support.
|
|
|
|
**/
|
|
UINT16
|
|
GetWidth (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
OUT UINT16 *AdjustWidth
|
|
)
|
|
{
|
|
CHAR16 *String;
|
|
UINTN Size;
|
|
EFI_IFR_TEXT *TestOp;
|
|
UINT16 ReturnWidth;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
//
|
|
// For modal form, clean the entire row.
|
|
//
|
|
if ((gFormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
if (AdjustWidth != NULL) {
|
|
*AdjustWidth = LEFT_SKIPPED_COLUMNS;
|
|
}
|
|
return (UINT16)(gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gModalSkipColumn + LEFT_SKIPPED_COLUMNS));
|
|
}
|
|
|
|
Size = 0;
|
|
|
|
//
|
|
// See if the second text parameter is really NULL
|
|
//
|
|
if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
|
|
TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
|
|
if (TestOp->TextTwo != 0) {
|
|
String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
|
|
Size = StrLen (String);
|
|
FreePool (String);
|
|
}
|
|
}
|
|
|
|
if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
|
|
//
|
|
// Allow a wide display if text op-code and no secondary text op-code
|
|
//
|
|
((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
|
|
) {
|
|
|
|
//
|
|
// Return the space width.
|
|
//
|
|
if (AdjustWidth != NULL) {
|
|
*AdjustWidth = 2;
|
|
}
|
|
//
|
|
// Keep consistent with current behavior.
|
|
//
|
|
ReturnWidth = (UINT16) (gPromptBlockWidth + gOptionBlockWidth - 2);
|
|
} else {
|
|
if (AdjustWidth != NULL) {
|
|
*AdjustWidth = 1;
|
|
}
|
|
|
|
ReturnWidth = (UINT16) (gPromptBlockWidth - 1);
|
|
}
|
|
|
|
//
|
|
// For nest in statement, should the subtitle indent.
|
|
//
|
|
if (MenuOption->NestInStatement) {
|
|
ReturnWidth -= SUBTITLE_INDENT;
|
|
}
|
|
|
|
return ReturnWidth;
|
|
}
|
|
|
|
/**
|
|
Will copy LineWidth amount of a string in the OutputString buffer and return the
|
|
number of CHAR16 characters that were copied into the OutputString buffer.
|
|
The output string format is:
|
|
Glyph Info + String info + '\0'.
|
|
|
|
In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
|
|
|
|
@param InputString String description for this option.
|
|
@param LineWidth Width of the desired string to extract in CHAR16
|
|
characters
|
|
@param GlyphWidth The glyph width of the begin of the char in the string.
|
|
@param Index Where in InputString to start the copy process
|
|
@param OutputString Buffer to copy the string into
|
|
|
|
@return Returns the number of CHAR16 characters that were copied into the OutputString
|
|
buffer, include extra glyph info and '\0' info.
|
|
|
|
**/
|
|
UINT16
|
|
GetLineByWidth (
|
|
IN CHAR16 *InputString,
|
|
IN UINT16 LineWidth,
|
|
IN OUT UINT16 *GlyphWidth,
|
|
IN OUT UINTN *Index,
|
|
OUT CHAR16 **OutputString
|
|
)
|
|
{
|
|
UINT16 StrOffset;
|
|
UINT16 GlyphOffset;
|
|
UINT16 OriginalGlyphWidth;
|
|
BOOLEAN ReturnFlag;
|
|
UINT16 LastSpaceOffset;
|
|
UINT16 LastGlyphWidth;
|
|
|
|
if (InputString == NULL || Index == NULL || OutputString == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
if (LineWidth == 0 || *GlyphWidth == 0) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Save original glyph width.
|
|
//
|
|
OriginalGlyphWidth = *GlyphWidth;
|
|
LastGlyphWidth = OriginalGlyphWidth;
|
|
ReturnFlag = FALSE;
|
|
LastSpaceOffset = 0;
|
|
|
|
//
|
|
// NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen.
|
|
// To avoid displaying this empty line in screen, just skip the two CHARs here.
|
|
//
|
|
if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
|
|
*Index = *Index + 2;
|
|
}
|
|
|
|
//
|
|
// Fast-forward the string and see if there is a carriage-return in the string
|
|
//
|
|
for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) {
|
|
switch (InputString[*Index + StrOffset]) {
|
|
case NARROW_CHAR:
|
|
*GlyphWidth = 1;
|
|
break;
|
|
|
|
case WIDE_CHAR:
|
|
*GlyphWidth = 2;
|
|
break;
|
|
|
|
case CHAR_CARRIAGE_RETURN:
|
|
case CHAR_LINEFEED:
|
|
case CHAR_NULL:
|
|
ReturnFlag = TRUE;
|
|
break;
|
|
|
|
default:
|
|
GlyphOffset = GlyphOffset + *GlyphWidth;
|
|
|
|
//
|
|
// Record the last space info in this line. Will be used in rewind.
|
|
//
|
|
if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) {
|
|
LastSpaceOffset = StrOffset;
|
|
LastGlyphWidth = *GlyphWidth;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (ReturnFlag) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Rewind the string from the maximum size until we see a space to break the line
|
|
//
|
|
if (GlyphOffset > LineWidth) {
|
|
//
|
|
// Rewind the string to last space char in this line.
|
|
//
|
|
if (LastSpaceOffset != 0) {
|
|
StrOffset = LastSpaceOffset;
|
|
*GlyphWidth = LastGlyphWidth;
|
|
} else {
|
|
//
|
|
// Roll back to last char in the line width.
|
|
//
|
|
StrOffset--;
|
|
}
|
|
}
|
|
|
|
//
|
|
// The CHAR_NULL has process last time, this time just return 0 to stand for the end.
|
|
//
|
|
if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Need extra glyph info and '\0' info, so +2.
|
|
//
|
|
*OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16)));
|
|
if (*OutputString == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Save the glyph info at the begin of the string, will used by Print function.
|
|
//
|
|
if (OriginalGlyphWidth == 1) {
|
|
*(*OutputString) = NARROW_CHAR;
|
|
} else {
|
|
*(*OutputString) = WIDE_CHAR;
|
|
}
|
|
|
|
CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16));
|
|
|
|
if (InputString[*Index + StrOffset] == CHAR_SPACE) {
|
|
//
|
|
// Skip the space info at the begin of next line.
|
|
//
|
|
*Index = (UINT16) (*Index + StrOffset + 1);
|
|
} else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
|
|
//
|
|
// Skip the /n or /n/r info.
|
|
//
|
|
if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
|
|
*Index = (UINT16) (*Index + StrOffset + 2);
|
|
} else {
|
|
*Index = (UINT16) (*Index + StrOffset + 1);
|
|
}
|
|
} else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
|
|
//
|
|
// Skip the /r or /r/n info.
|
|
//
|
|
if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
|
|
*Index = (UINT16) (*Index + StrOffset + 2);
|
|
} else {
|
|
*Index = (UINT16) (*Index + StrOffset + 1);
|
|
}
|
|
} else {
|
|
*Index = (UINT16) (*Index + StrOffset);
|
|
}
|
|
|
|
//
|
|
// Include extra glyph info and '\0' info, so +2.
|
|
//
|
|
return StrOffset + 2;
|
|
}
|
|
|
|
/**
|
|
Add one menu option by specified description and context.
|
|
|
|
@param Statement Statement of this Menu Option.
|
|
@param MenuItemCount The index for this Option in the Menu.
|
|
@param NestIn Whether this statement is nest in another statement.
|
|
|
|
**/
|
|
VOID
|
|
UiAddMenuOption (
|
|
IN FORM_DISPLAY_ENGINE_STATEMENT *Statement,
|
|
IN UINT16 *MenuItemCount,
|
|
IN BOOLEAN NestIn
|
|
)
|
|
{
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN Index;
|
|
UINTN Count;
|
|
CHAR16 *String;
|
|
UINT16 NumberOfLines;
|
|
UINT16 GlyphWidth;
|
|
UINT16 Width;
|
|
UINTN ArrayEntry;
|
|
CHAR16 *OutputString;
|
|
EFI_STRING_ID PromptId;
|
|
|
|
NumberOfLines = 1;
|
|
ArrayEntry = 0;
|
|
GlyphWidth = 1;
|
|
Count = 1;
|
|
MenuOption = NULL;
|
|
|
|
PromptId = GetPrompt (Statement->OpCode);
|
|
ASSERT (PromptId != 0);
|
|
|
|
String = GetToken (PromptId, gFormData->HiiHandle);
|
|
ASSERT (String != NULL);
|
|
|
|
if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
|
|
Count = 3;
|
|
}
|
|
|
|
for (Index = 0; Index < Count; Index++) {
|
|
MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
|
|
ASSERT (MenuOption);
|
|
|
|
MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
|
|
MenuOption->Description = String;
|
|
MenuOption->Handle = gFormData->HiiHandle;
|
|
MenuOption->ThisTag = Statement;
|
|
MenuOption->NestInStatement = NestIn;
|
|
MenuOption->EntryNumber = *MenuItemCount;
|
|
|
|
MenuOption->Sequence = Index;
|
|
|
|
if ((Statement->Attribute & HII_DISPLAY_GRAYOUT) != 0) {
|
|
MenuOption->GrayOut = TRUE;
|
|
} else {
|
|
MenuOption->GrayOut = FALSE;
|
|
}
|
|
|
|
if ((Statement->Attribute & HII_DISPLAY_LOCK) != 0 || (gFormData->Attribute & HII_DISPLAY_LOCK) != 0) {
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
|
|
//
|
|
// If the form or the question has the lock attribute, deal same as grayout.
|
|
//
|
|
if ((gFormData->Attribute & HII_DISPLAY_LOCK) != 0 || (Statement->Attribute & HII_DISPLAY_LOCK) != 0) {
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
|
|
switch (Statement->OpCode->OpCode) {
|
|
case EFI_IFR_ORDERED_LIST_OP:
|
|
case EFI_IFR_ONE_OF_OP:
|
|
case EFI_IFR_NUMERIC_OP:
|
|
case EFI_IFR_TIME_OP:
|
|
case EFI_IFR_DATE_OP:
|
|
case EFI_IFR_CHECKBOX_OP:
|
|
case EFI_IFR_PASSWORD_OP:
|
|
case EFI_IFR_STRING_OP:
|
|
//
|
|
// User could change the value of these items
|
|
//
|
|
MenuOption->IsQuestion = TRUE;
|
|
break;
|
|
case EFI_IFR_TEXT_OP:
|
|
if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
|
|
//
|
|
// Initializing GrayOut option as TRUE for Text setup options
|
|
// so that those options will be Gray in colour and un selectable.
|
|
//
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
MenuOption->IsQuestion = FALSE;
|
|
break;
|
|
}
|
|
|
|
if ((Statement->Attribute & HII_DISPLAY_READONLY) != 0) {
|
|
MenuOption->ReadOnly = TRUE;
|
|
if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) {
|
|
MenuOption->GrayOut = TRUE;
|
|
}
|
|
}
|
|
|
|
if (Index == 0 &&
|
|
(Statement->OpCode->OpCode != EFI_IFR_DATE_OP) &&
|
|
(Statement->OpCode->OpCode != EFI_IFR_TIME_OP)) {
|
|
Width = GetWidth (MenuOption, NULL);
|
|
for (; GetLineByWidth (String, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) {
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&String[ArrayEntry]) != 0) {
|
|
NumberOfLines++;
|
|
}
|
|
FreePool (OutputString);
|
|
}
|
|
} else {
|
|
//
|
|
// Add three MenuOptions for Date/Time
|
|
// Data format : [01/02/2004] [11:22:33]
|
|
// Line number : 0 0 1 0 0 1
|
|
//
|
|
NumberOfLines = 0;
|
|
}
|
|
|
|
if (Index == 2) {
|
|
//
|
|
// Override LineNumber for the MenuOption in Date/Time sequence
|
|
//
|
|
MenuOption->Skip = 1;
|
|
} else {
|
|
MenuOption->Skip = NumberOfLines;
|
|
}
|
|
|
|
InsertTailList (&gMenuOption, &MenuOption->Link);
|
|
}
|
|
|
|
(*MenuItemCount)++;
|
|
}
|
|
|
|
/**
|
|
Create the menu list base on the form data info.
|
|
|
|
**/
|
|
VOID
|
|
ConvertStatementToMenu (
|
|
VOID
|
|
)
|
|
{
|
|
UINT16 MenuItemCount;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NestLink;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *NestStatement;
|
|
|
|
MenuItemCount = 0;
|
|
InitializeListHead (&gMenuOption);
|
|
|
|
Link = GetFirstNode (&gFormData->StatementListHead);
|
|
while (!IsNull (&gFormData->StatementListHead, Link)) {
|
|
Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link);
|
|
Link = GetNextNode (&gFormData->StatementListHead, Link);
|
|
|
|
//
|
|
// Skip the opcode not recognized by Display core.
|
|
//
|
|
if (Statement->OpCode->OpCode == EFI_IFR_GUID_OP) {
|
|
continue;
|
|
}
|
|
|
|
UiAddMenuOption (Statement, &MenuItemCount, FALSE);
|
|
|
|
//
|
|
// Check the statement nest in this host statement.
|
|
//
|
|
NestLink = GetFirstNode (&Statement->NestStatementList);
|
|
while (!IsNull (&Statement->NestStatementList, NestLink)) {
|
|
NestStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (NestLink);
|
|
NestLink = GetNextNode (&Statement->NestStatementList, NestLink);
|
|
|
|
//
|
|
// Skip the opcode not recognized by Display core.
|
|
//
|
|
if (NestStatement->OpCode->OpCode == EFI_IFR_GUID_OP) {
|
|
continue;
|
|
}
|
|
|
|
UiAddMenuOption (NestStatement, &MenuItemCount, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Count the storage space of a Unicode string.
|
|
|
|
This function handles the Unicode string with NARROW_CHAR
|
|
and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR
|
|
does not count in the resultant output. If a WIDE_CHAR is
|
|
hit, then 2 Unicode character will consume an output storage
|
|
space with size of CHAR16 till a NARROW_CHAR is hit.
|
|
|
|
If String is NULL, then ASSERT ().
|
|
|
|
@param String The input string to be counted.
|
|
|
|
@return Storage space for the input string.
|
|
|
|
**/
|
|
UINTN
|
|
GetStringWidth (
|
|
IN CHAR16 *String
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
UINTN IncrementValue;
|
|
|
|
ASSERT (String != NULL);
|
|
if (String == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
Index = 0;
|
|
Count = 0;
|
|
IncrementValue = 1;
|
|
|
|
do {
|
|
//
|
|
// Advance to the null-terminator or to the first width directive
|
|
//
|
|
for (;
|
|
(String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0);
|
|
Index++, Count = Count + IncrementValue
|
|
)
|
|
;
|
|
|
|
//
|
|
// We hit the null-terminator, we now have a count
|
|
//
|
|
if (String[Index] == 0) {
|
|
break;
|
|
}
|
|
//
|
|
// We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
|
|
// and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
|
|
//
|
|
if (String[Index] == NARROW_CHAR) {
|
|
//
|
|
// Skip to the next character
|
|
//
|
|
Index++;
|
|
IncrementValue = 1;
|
|
} else {
|
|
//
|
|
// Skip to the next character
|
|
//
|
|
Index++;
|
|
IncrementValue = 2;
|
|
}
|
|
} while (String[Index] != 0);
|
|
|
|
//
|
|
// Increment by one to include the null-terminator in the size
|
|
//
|
|
Count++;
|
|
|
|
return Count * sizeof (CHAR16);
|
|
}
|
|
|
|
/**
|
|
Base on the input option string to update the skip value for a menu option.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
@param OptionString The input option string.
|
|
|
|
**/
|
|
VOID
|
|
UpdateSkipInfoForMenu (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN CHAR16 *OptionString
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINT16 Width;
|
|
UINTN Row;
|
|
CHAR16 *OutputString;
|
|
UINT16 GlyphWidth;
|
|
|
|
Width = (UINT16) gOptionBlockWidth;
|
|
GlyphWidth = 1;
|
|
Row = 1;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
|
|
if (StrLen (&OptionString[Index]) != 0) {
|
|
Row++;
|
|
}
|
|
|
|
FreePool (OutputString);
|
|
}
|
|
|
|
if ((Row > MenuOption->Skip) &&
|
|
(MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_DATE_OP) &&
|
|
(MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_TIME_OP)) {
|
|
MenuOption->Skip = Row;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Update display lines for a Menu Option.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
|
|
**/
|
|
VOID
|
|
UpdateOptionSkipLines (
|
|
IN UI_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
CHAR16 *OptionString;
|
|
|
|
OptionString = NULL;
|
|
|
|
ProcessOptions (MenuOption, FALSE, &OptionString, TRUE);
|
|
if (OptionString != NULL) {
|
|
UpdateSkipInfoForMenu (MenuOption, OptionString);
|
|
|
|
FreePool (OptionString);
|
|
}
|
|
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
|
|
OptionString = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
|
|
|
|
if (OptionString != NULL) {
|
|
UpdateSkipInfoForMenu (MenuOption, OptionString);
|
|
|
|
FreePool (OptionString);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Check whether this Menu Option could be print.
|
|
|
|
Check Prompt string, option string or text two string not NULL.
|
|
|
|
This is an internal function.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
|
|
@retval TRUE This Menu Option is printable.
|
|
@retval FALSE This Menu Option could not be printable.
|
|
|
|
**/
|
|
BOOLEAN
|
|
PrintableMenu (
|
|
UI_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_STRING OptionString;
|
|
|
|
OptionString = NULL;
|
|
|
|
if (MenuOption->Description[0] != '\0') {
|
|
return TRUE;
|
|
}
|
|
|
|
Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
return FALSE;
|
|
}
|
|
if (OptionString != NULL && OptionString[0] != '\0') {
|
|
FreePool (OptionString);
|
|
return TRUE;
|
|
}
|
|
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
|
|
OptionString = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
|
|
ASSERT (OptionString != NULL);
|
|
if (OptionString[0] != '\0'){
|
|
FreePool (OptionString);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Check whether this Menu Option could be highlighted.
|
|
|
|
This is an internal function.
|
|
|
|
@param MenuOption The MenuOption to be checked.
|
|
|
|
@retval TRUE This Menu Option is selectable.
|
|
@retval FALSE This Menu Option could not be selected.
|
|
|
|
**/
|
|
BOOLEAN
|
|
IsSelectable (
|
|
UI_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
|
|
MenuOption->GrayOut || MenuOption->ReadOnly || !PrintableMenu (MenuOption)) {
|
|
return FALSE;
|
|
} else {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Move to next selectable statement.
|
|
|
|
This is an internal function.
|
|
|
|
@param GoUp The navigation direction. TRUE: up, FALSE: down.
|
|
@param CurrentPosition Current position.
|
|
@param GapToTop Gap position to top or bottom.
|
|
@param FindInForm Whether find menu in current form or beyond.
|
|
|
|
@return The row distance from current MenuOption to next selectable MenuOption.
|
|
|
|
@retval -1 Reach the begin of the menu, still can't find the selectable menu.
|
|
@retval Value Find the selectable menu, maybe the truly selectable, maybe the
|
|
first menu showing beyond current form or last menu showing in
|
|
current form.
|
|
The value is the line number between the new selected menu and the
|
|
current select menu, not include the new selected menu.
|
|
|
|
**/
|
|
INTN
|
|
MoveToNextStatement (
|
|
IN BOOLEAN GoUp,
|
|
IN OUT LIST_ENTRY **CurrentPosition,
|
|
IN UINTN GapToTop,
|
|
IN BOOLEAN FindInForm
|
|
)
|
|
{
|
|
INTN Distance;
|
|
LIST_ENTRY *Pos;
|
|
UI_MENU_OPTION *NextMenuOption;
|
|
UI_MENU_OPTION *PreMenuOption;
|
|
|
|
Distance = 0;
|
|
Pos = *CurrentPosition;
|
|
|
|
if (Pos == &gMenuOption) {
|
|
return -1;
|
|
}
|
|
|
|
PreMenuOption = MENU_OPTION_FROM_LINK (Pos);
|
|
|
|
while (TRUE) {
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
|
|
//
|
|
// NextMenuOption->Row == 0 means this menu has not calculate
|
|
// the NextMenuOption->Skip value yet, just calculate here.
|
|
//
|
|
if (NextMenuOption->Row == 0) {
|
|
UpdateOptionSkipLines (NextMenuOption);
|
|
}
|
|
|
|
if (IsSelectable (NextMenuOption)) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// In this case, still can't find the selectable menu,
|
|
// return the first one beyond the showing form.
|
|
//
|
|
if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
|
|
if (FindInForm) {
|
|
NextMenuOption = PreMenuOption;
|
|
}
|
|
break;
|
|
}
|
|
|
|
Distance += NextMenuOption->Skip;
|
|
|
|
//
|
|
// Arrive at begin of the menu list.
|
|
//
|
|
if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
|
|
Distance = -1;
|
|
break;
|
|
}
|
|
|
|
Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
|
|
PreMenuOption = NextMenuOption;
|
|
}
|
|
|
|
*CurrentPosition = &NextMenuOption->Link;
|
|
return Distance;
|
|
}
|
|
|
|
|
|
/**
|
|
Process option string for date/time opcode.
|
|
|
|
@param MenuOption Menu option point to date/time.
|
|
@param OptionString Option string input for process.
|
|
@param AddOptCol Whether need to update MenuOption->OptCol.
|
|
|
|
**/
|
|
VOID
|
|
ProcessStringForDateTime (
|
|
UI_MENU_OPTION *MenuOption,
|
|
CHAR16 *OptionString,
|
|
BOOLEAN AddOptCol
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UINTN Count;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
EFI_IFR_DATE *Date;
|
|
EFI_IFR_TIME *Time;
|
|
|
|
ASSERT (MenuOption != NULL && OptionString != NULL);
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
Date = NULL;
|
|
Time = NULL;
|
|
if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
|
|
Date = (EFI_IFR_DATE *) Statement->OpCode;
|
|
} else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
|
|
Time = (EFI_IFR_TIME *) Statement->OpCode;
|
|
}
|
|
|
|
//
|
|
// If leading spaces on OptionString - remove the spaces
|
|
//
|
|
for (Index = 0; OptionString[Index] == L' '; Index++) {
|
|
//
|
|
// Base on the blockspace to get the option column info.
|
|
//
|
|
if (AddOptCol) {
|
|
MenuOption->OptCol++;
|
|
}
|
|
}
|
|
|
|
for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
|
|
OptionString[Count] = OptionString[Index];
|
|
Count++;
|
|
}
|
|
OptionString[Count] = CHAR_NULL;
|
|
|
|
//
|
|
// Enable to suppress field in the opcode base on the flag.
|
|
//
|
|
if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
|
|
//
|
|
// OptionString format is: <**: **: ****>
|
|
// |month|day|year|
|
|
// 4 3 5
|
|
//
|
|
if ((Date->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
|
|
//
|
|
// At this point, only "<**:" in the optionstring.
|
|
// Clean the day's ** field, after clean, the format is "< :"
|
|
//
|
|
SetUnicodeMem (&OptionString[1], 2, L' ');
|
|
} else if ((Date->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
|
|
//
|
|
// At this point, only "**:" in the optionstring.
|
|
// Clean the month's "**" field, after clean, the format is " :"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
} else if ((Date->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
|
|
//
|
|
// At this point, only "****>" in the optionstring.
|
|
// Clean the year's "****" field, after clean, the format is " >"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 4, L' ');
|
|
}
|
|
} else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
|
|
//
|
|
// OptionString format is: <**: **: **>
|
|
// |hour|minute|second|
|
|
// 4 3 3
|
|
//
|
|
if ((Time->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
|
|
//
|
|
// At this point, only "<**:" in the optionstring.
|
|
// Clean the hour's ** field, after clean, the format is "< :"
|
|
//
|
|
SetUnicodeMem (&OptionString[1], 2, L' ');
|
|
} else if ((Time->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
|
|
//
|
|
// At this point, only "**:" in the optionstring.
|
|
// Clean the minute's "**" field, after clean, the format is " :"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
} else if ((Time->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
|
|
//
|
|
// At this point, only "**>" in the optionstring.
|
|
// Clean the second's "**" field, after clean, the format is " >"
|
|
//
|
|
SetUnicodeMem (&OptionString[0], 2, L' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
Adjust Data and Time position accordingly.
|
|
Data format : [01/02/2004] [11:22:33]
|
|
Line number : 0 0 1 0 0 1
|
|
|
|
This is an internal function.
|
|
|
|
@param DirectionUp the up or down direction. False is down. True is
|
|
up.
|
|
@param CurrentPosition Current position. On return: Point to the last
|
|
Option (Year or Second) if up; Point to the first
|
|
Option (Month or Hour) if down.
|
|
|
|
@return Return line number to pad. It is possible that we stand on a zero-advance
|
|
@return data or time opcode, so pad one line when we judge if we are going to scroll outside.
|
|
|
|
**/
|
|
UINTN
|
|
AdjustDateAndTimePosition (
|
|
IN BOOLEAN DirectionUp,
|
|
IN OUT LIST_ENTRY **CurrentPosition
|
|
)
|
|
{
|
|
UINTN Count;
|
|
LIST_ENTRY *NewPosition;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UINTN PadLineNumber;
|
|
|
|
PadLineNumber = 0;
|
|
NewPosition = *CurrentPosition;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
|
|
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) ||
|
|
(MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
|
|
//
|
|
// Calculate the distance from current position to the last Date/Time MenuOption
|
|
//
|
|
Count = 0;
|
|
while (MenuOption->Skip == 0) {
|
|
Count++;
|
|
NewPosition = NewPosition->ForwardLink;
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
|
|
PadLineNumber = 1;
|
|
}
|
|
|
|
NewPosition = *CurrentPosition;
|
|
if (DirectionUp) {
|
|
//
|
|
// Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
|
|
// to be one that back to the previous set of MenuOptions, we need to advance to the first
|
|
// Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
|
|
// checking can be done.
|
|
//
|
|
while (Count++ < 2) {
|
|
NewPosition = NewPosition->BackLink;
|
|
}
|
|
} else {
|
|
//
|
|
// Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
|
|
// to be one that progresses to the next set of MenuOptions, we need to advance to the last
|
|
// Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
|
|
// checking can be done.
|
|
//
|
|
while (Count-- > 0) {
|
|
NewPosition = NewPosition->ForwardLink;
|
|
}
|
|
}
|
|
|
|
*CurrentPosition = NewPosition;
|
|
}
|
|
|
|
return PadLineNumber;
|
|
}
|
|
|
|
/**
|
|
Get step info from numeric opcode.
|
|
|
|
@param[in] OpCode The input numeric op code.
|
|
|
|
@return step info for this opcode.
|
|
**/
|
|
UINT64
|
|
GetFieldFromNum (
|
|
IN EFI_IFR_OP_HEADER *OpCode
|
|
)
|
|
{
|
|
EFI_IFR_NUMERIC *NumericOp;
|
|
UINT64 Step;
|
|
|
|
NumericOp = (EFI_IFR_NUMERIC *) OpCode;
|
|
|
|
switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
|
|
case EFI_IFR_NUMERIC_SIZE_1:
|
|
Step = NumericOp->data.u8.Step;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_2:
|
|
Step = NumericOp->data.u16.Step;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_4:
|
|
Step = NumericOp->data.u32.Step;
|
|
break;
|
|
|
|
case EFI_IFR_NUMERIC_SIZE_8:
|
|
Step = NumericOp->data.u64.Step;
|
|
break;
|
|
|
|
default:
|
|
Step = 0;
|
|
break;
|
|
}
|
|
|
|
return Step;
|
|
}
|
|
|
|
/**
|
|
Find the registered HotKey based on KeyData.
|
|
|
|
@param[in] KeyData A pointer to a buffer that describes the keystroke
|
|
information for the hot key.
|
|
|
|
@return The registered HotKey context. If no found, NULL will return.
|
|
**/
|
|
BROWSER_HOT_KEY *
|
|
GetHotKeyFromRegisterList (
|
|
IN EFI_INPUT_KEY *KeyData
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
BROWSER_HOT_KEY *HotKey;
|
|
|
|
Link = GetFirstNode (&gFormData->HotKeyListHead);
|
|
while (!IsNull (&gFormData->HotKeyListHead, Link)) {
|
|
HotKey = BROWSER_HOT_KEY_FROM_LINK (Link);
|
|
|
|
if (HotKey->KeyData->ScanCode == KeyData->ScanCode) {
|
|
return HotKey;
|
|
}
|
|
|
|
Link = GetNextNode (&gFormData->HotKeyListHead, Link);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
Determine if the menu is the last menu that can be selected.
|
|
|
|
This is an internal function.
|
|
|
|
@param Direction The scroll direction. False is down. True is up.
|
|
@param CurrentPos The current focus.
|
|
|
|
@return FALSE -- the menu isn't the last menu that can be selected.
|
|
@return TRUE -- the menu is the last menu that can be selected.
|
|
|
|
**/
|
|
BOOLEAN
|
|
ValueIsScroll (
|
|
IN BOOLEAN Direction,
|
|
IN LIST_ENTRY *CurrentPos
|
|
)
|
|
{
|
|
LIST_ENTRY *Temp;
|
|
|
|
Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
|
|
|
|
if (Temp == &gMenuOption) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Wait for a given event to fire, or for an optional timeout to expire.
|
|
|
|
@param Event The event to wait for
|
|
|
|
@retval UI_EVENT_TYPE The type of the event which is trigged.
|
|
|
|
**/
|
|
UI_EVENT_TYPE
|
|
UiWaitForEvent (
|
|
IN EFI_EVENT Event
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN EventNum;
|
|
UINT64 Timeout;
|
|
EFI_EVENT TimerEvent;
|
|
EFI_EVENT WaitList[3];
|
|
UI_EVENT_TYPE EventType;
|
|
|
|
TimerEvent = NULL;
|
|
Timeout = FormExitTimeout(gFormData);
|
|
|
|
if (Timeout != 0) {
|
|
Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
|
|
|
|
//
|
|
// Set the timer event
|
|
//
|
|
gBS->SetTimer (
|
|
TimerEvent,
|
|
TimerRelative,
|
|
Timeout
|
|
);
|
|
}
|
|
|
|
WaitList[0] = Event;
|
|
EventNum = 1;
|
|
if (gFormData->FormRefreshEvent != NULL) {
|
|
WaitList[EventNum] = gFormData->FormRefreshEvent;
|
|
EventNum ++;
|
|
}
|
|
|
|
if (Timeout != 0) {
|
|
WaitList[EventNum] = TimerEvent;
|
|
EventNum ++;
|
|
}
|
|
|
|
Status = gBS->WaitForEvent (EventNum, WaitList, &Index);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
switch (Index) {
|
|
case 0:
|
|
EventType = UIEventKey;
|
|
break;
|
|
|
|
case 1:
|
|
if (gFormData->FormRefreshEvent != NULL) {
|
|
EventType = UIEventDriver;
|
|
} else {
|
|
ASSERT (Timeout != 0 && EventNum == 2);
|
|
EventType = UIEventTimeOut;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT (Index == 2 && EventNum == 3);
|
|
EventType = UIEventTimeOut;
|
|
break;
|
|
}
|
|
|
|
if (Timeout != 0) {
|
|
gBS->CloseEvent (TimerEvent);
|
|
}
|
|
|
|
return EventType;
|
|
}
|
|
|
|
/**
|
|
Get question id info from the input opcode header.
|
|
|
|
@param OpCode The input opcode header pointer.
|
|
|
|
@retval The question id for this opcode.
|
|
|
|
**/
|
|
EFI_QUESTION_ID
|
|
GetQuestionIdInfo (
|
|
IN EFI_IFR_OP_HEADER *OpCode
|
|
)
|
|
{
|
|
EFI_IFR_QUESTION_HEADER *QuestionHeader;
|
|
|
|
if (OpCode->Length < sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)) {
|
|
return 0;
|
|
}
|
|
|
|
QuestionHeader = (EFI_IFR_QUESTION_HEADER *)((UINT8 *) OpCode + sizeof(EFI_IFR_OP_HEADER));
|
|
|
|
return QuestionHeader->QuestionId;
|
|
}
|
|
|
|
|
|
/**
|
|
Find the top of screen menu base on the current menu.
|
|
|
|
@param CurPos Current input menu.
|
|
@param Rows Totol screen rows.
|
|
@param SkipValue SkipValue for this new form.
|
|
|
|
@retval TopOfScreen Top of screen menu for the new form.
|
|
|
|
**/
|
|
LIST_ENTRY *
|
|
FindTopOfScreenMenu (
|
|
IN LIST_ENTRY *CurPos,
|
|
IN UINTN Rows,
|
|
OUT UINTN *SkipValue
|
|
)
|
|
{
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *TopOfScreen;
|
|
UI_MENU_OPTION *PreviousMenuOption;
|
|
|
|
Link = CurPos;
|
|
PreviousMenuOption = NULL;
|
|
|
|
while (Link->BackLink != &gMenuOption) {
|
|
Link = Link->BackLink;
|
|
PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
if (PreviousMenuOption->Row == 0) {
|
|
UpdateOptionSkipLines (PreviousMenuOption);
|
|
}
|
|
if (Rows <= PreviousMenuOption->Skip) {
|
|
break;
|
|
}
|
|
Rows = Rows - PreviousMenuOption->Skip;
|
|
}
|
|
|
|
if (Link->BackLink == &gMenuOption) {
|
|
TopOfScreen = gMenuOption.ForwardLink;
|
|
if (PreviousMenuOption != NULL && Rows < PreviousMenuOption->Skip) {
|
|
*SkipValue = PreviousMenuOption->Skip - Rows;
|
|
} else {
|
|
*SkipValue = 0;
|
|
}
|
|
} else {
|
|
TopOfScreen = Link;
|
|
*SkipValue = PreviousMenuOption->Skip - Rows;
|
|
}
|
|
|
|
return TopOfScreen;
|
|
}
|
|
|
|
/**
|
|
Find the first menu which will be show at the top.
|
|
|
|
@param FormData The data info for this form.
|
|
@param TopOfScreen The link_entry pointer to top menu.
|
|
@param HighlightMenu The menu which will be highlight.
|
|
@param SkipValue The skip value for the top menu.
|
|
|
|
**/
|
|
VOID
|
|
FindTopMenu (
|
|
IN FORM_DISPLAY_ENGINE_FORM *FormData,
|
|
OUT LIST_ENTRY **TopOfScreen,
|
|
OUT LIST_ENTRY **HighlightMenu,
|
|
OUT UINTN *SkipValue
|
|
)
|
|
{
|
|
LIST_ENTRY *NewPos;
|
|
UINTN TopRow;
|
|
UINTN BottomRow;
|
|
UI_MENU_OPTION *SavedMenuOption;
|
|
UINTN TmpValue;
|
|
|
|
TmpValue = 0;
|
|
TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT;
|
|
BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT;
|
|
|
|
//
|
|
// If not has input highlight statement, just return the first one in this form.
|
|
//
|
|
if (FormData->HighLightedStatement == NULL) {
|
|
*TopOfScreen = gMenuOption.ForwardLink;
|
|
*HighlightMenu = gMenuOption.ForwardLink;
|
|
if (!IsListEmpty (&gMenuOption)) {
|
|
MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE);
|
|
}
|
|
*SkipValue = 0;
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Now base on the input highlight menu to find the top menu in this page.
|
|
// Will base on the highlight menu show at the bottom to find the top menu.
|
|
//
|
|
NewPos = gMenuOption.ForwardLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
|
|
while ((SavedMenuOption->ThisTag != FormData->HighLightedStatement) ||
|
|
(SavedMenuOption->Sequence != gSequence)) {
|
|
NewPos = NewPos->ForwardLink;
|
|
if (NewPos == &gMenuOption) {
|
|
//
|
|
// Not Found it, break
|
|
//
|
|
break;
|
|
}
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
}
|
|
ASSERT (SavedMenuOption->ThisTag == FormData->HighLightedStatement);
|
|
|
|
*HighlightMenu = NewPos;
|
|
|
|
AdjustDateAndTimePosition(FALSE, &NewPos);
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
UpdateOptionSkipLines (SavedMenuOption);
|
|
|
|
//
|
|
// FormRefreshEvent != NULL means this form will auto exit at an interval, display engine
|
|
// will try to keep highlight on the current position after this form exit and re-enter.
|
|
//
|
|
// HiiHandle + QuestionId can find the only one question in the system.
|
|
//
|
|
// If this question has question id, save the question id info to find the question.
|
|
// else save the opcode buffer to find it.
|
|
//
|
|
if (gFormData->FormRefreshEvent != NULL && gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle) {
|
|
if (gHighligthMenuInfo.QuestionId != 0) {
|
|
if (gHighligthMenuInfo.QuestionId == GetQuestionIdInfo(SavedMenuOption->ThisTag->OpCode)) {
|
|
BottomRow = gHighligthMenuInfo.DisplayRow + SavedMenuOption->Skip;
|
|
//
|
|
// SkipValue only used for menu at the top of the form.
|
|
// If Highlight menu is not at the top, this value will be update later.
|
|
//
|
|
TmpValue = gHighligthMenuInfo.SkipValue;
|
|
}
|
|
} else if (gHighligthMenuInfo.OpCode != NULL){
|
|
if (!CompareMem (gHighligthMenuInfo.OpCode, SavedMenuOption->ThisTag->OpCode, gHighligthMenuInfo.OpCode->Length)) {
|
|
BottomRow = gHighligthMenuInfo.DisplayRow + SavedMenuOption->Skip;
|
|
//
|
|
// SkipValue only used for menu at the top of the form.
|
|
// If Highlight menu is not at the top, this value will be update later.
|
|
//
|
|
TmpValue = gHighligthMenuInfo.SkipValue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (SavedMenuOption->Skip >= BottomRow - TopRow) {
|
|
*TopOfScreen = NewPos;
|
|
} else {
|
|
*TopOfScreen = FindTopOfScreenMenu(NewPos, BottomRow - TopRow - SavedMenuOption->Skip, &TmpValue);
|
|
}
|
|
AdjustDateAndTimePosition(TRUE, TopOfScreen);
|
|
|
|
*SkipValue = TmpValue;
|
|
}
|
|
|
|
/**
|
|
Update highlight menu info.
|
|
|
|
@param MenuOption The menu opton which is highlight.
|
|
@param SkipValue The skipvalue info for this menu.
|
|
SkipValue only used for the menu at the top of the form.
|
|
|
|
**/
|
|
VOID
|
|
UpdateHighlightMenuInfo (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN UINTN SkipValue
|
|
)
|
|
{
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
|
|
//
|
|
// This is the current selected statement
|
|
//
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
//
|
|
// Get the highlight statement.
|
|
//
|
|
gUserInput->SelectedStatement = Statement;
|
|
gSequence = (UINT16) MenuOption->Sequence;
|
|
|
|
//
|
|
// FormRefreshEvent != NULL means this form will auto exit at an interval, display engine
|
|
// will try to keep highlight on the current position after this form exit and re-enter.
|
|
//
|
|
// HiiHandle + QuestionId can find the only one question in the system.
|
|
//
|
|
// If this question has question id, base on the question id info to find the question.
|
|
// else base on the opcode buffer to find it.
|
|
//
|
|
if (gFormData->FormRefreshEvent != NULL) {
|
|
gHighligthMenuInfo.HiiHandle = gFormData->HiiHandle;
|
|
gHighligthMenuInfo.QuestionId = GetQuestionIdInfo(Statement->OpCode);
|
|
|
|
//
|
|
// if question id == 0, save the opcode buffer for later use.
|
|
//
|
|
if (gHighligthMenuInfo.QuestionId == 0) {
|
|
if (gHighligthMenuInfo.OpCode != NULL) {
|
|
FreePool (gHighligthMenuInfo.OpCode);
|
|
}
|
|
gHighligthMenuInfo.OpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode);
|
|
ASSERT (gHighligthMenuInfo.OpCode != NULL);
|
|
}
|
|
gHighligthMenuInfo.DisplayRow = (UINT16) MenuOption->Row;
|
|
gHighligthMenuInfo.SkipValue = (UINT16) SkipValue;
|
|
} else {
|
|
gHighligthMenuInfo.HiiHandle = NULL;
|
|
gHighligthMenuInfo.QuestionId = 0;
|
|
if (gHighligthMenuInfo.OpCode != NULL) {
|
|
FreePool (gHighligthMenuInfo.OpCode);
|
|
gHighligthMenuInfo.OpCode = NULL;
|
|
}
|
|
gHighligthMenuInfo.DisplayRow = 0;
|
|
gHighligthMenuInfo.SkipValue = 0;
|
|
}
|
|
|
|
RefreshKeyHelp(gFormData, Statement, FALSE);
|
|
}
|
|
|
|
/**
|
|
Update attribut for this menu.
|
|
|
|
@param MenuOption The menu opton which this attribut used to.
|
|
@param Highlight Whether this menu will be highlight.
|
|
|
|
**/
|
|
VOID
|
|
SetDisplayAttribute (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN BOOLEAN Highlight
|
|
)
|
|
{
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
if (Highlight) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
|
|
return;
|
|
}
|
|
|
|
if (MenuOption->GrayOut) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ());
|
|
} else {
|
|
if (Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ());
|
|
} else {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Print string for this menu option.
|
|
|
|
@param MenuOption The menu opton which this attribut used to.
|
|
@param Col The column that this string will be print at.
|
|
@param Row The row that this string will be print at.
|
|
@param String The string which need to print.
|
|
@param Width The width need to print, if string is less than the
|
|
width, the block space will be used.
|
|
@param Highlight Whether this menu will be highlight.
|
|
|
|
**/
|
|
VOID
|
|
DisplayMenuString (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN UINTN Col,
|
|
IN UINTN Row,
|
|
IN CHAR16 *String,
|
|
IN UINTN Width,
|
|
IN BOOLEAN Highlight
|
|
)
|
|
{
|
|
UINTN Length;
|
|
|
|
//
|
|
// Print string with normal color.
|
|
//
|
|
if (!Highlight) {
|
|
PrintStringAtWithWidth (Col, Row, String, Width);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Print the highlight menu string.
|
|
// First print the highlight string.
|
|
//
|
|
SetDisplayAttribute(MenuOption, TRUE);
|
|
Length = PrintStringAt (Col, Row, String);
|
|
|
|
//
|
|
// Second, clean the empty after the string.
|
|
//
|
|
SetDisplayAttribute(MenuOption, FALSE);
|
|
PrintStringAtWithWidth (Col + Length, Row, L"", Width - Length);
|
|
}
|
|
|
|
/**
|
|
Check whether this menu can has option string.
|
|
|
|
@param MenuOption The menu opton which this attribut used to.
|
|
|
|
@retval TRUE This menu option can have option string.
|
|
@retval FALSE This menu option can't have option string.
|
|
|
|
**/
|
|
BOOLEAN
|
|
HasOptionString (
|
|
IN UI_MENU_OPTION *MenuOption
|
|
)
|
|
{
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
CHAR16 *String;
|
|
UINTN Size;
|
|
EFI_IFR_TEXT *TestOp;
|
|
|
|
Size = 0;
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
//
|
|
// See if the second text parameter is really NULL
|
|
//
|
|
if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
|
|
TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
|
|
if (TestOp->TextTwo != 0) {
|
|
String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
|
|
Size = StrLen (String);
|
|
FreePool (String);
|
|
}
|
|
}
|
|
|
|
if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
|
|
(Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
|
|
//
|
|
// Allow a wide display if text op-code and no secondary text op-code
|
|
//
|
|
((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
|
|
) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/**
|
|
Print string for this menu option.
|
|
|
|
@param MenuOption The menu opton which this attribut used to.
|
|
@param SkipWidth The skip width between the left to the start of the prompt.
|
|
@param BeginCol The begin column for one menu.
|
|
@param SkipLine The skip line for this menu.
|
|
@param BottomRow The bottom row for this form.
|
|
@param Highlight Whether this menu will be highlight.
|
|
@param UpdateCol Whether need to update the column info for Date/Time.
|
|
|
|
@retval EFI_SUCESSS Process the user selection success.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
DisplayOneMenu (
|
|
IN UI_MENU_OPTION *MenuOption,
|
|
IN UINTN SkipWidth,
|
|
IN UINTN BeginCol,
|
|
IN UINTN SkipLine,
|
|
IN UINTN BottomRow,
|
|
IN BOOLEAN Highlight,
|
|
IN BOOLEAN UpdateCol
|
|
)
|
|
{
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
UINTN Index;
|
|
UINT16 Width;
|
|
UINT16 PromptWidth;
|
|
CHAR16 *StringPtr;
|
|
CHAR16 *OptionString;
|
|
CHAR16 *OutputString;
|
|
UINT16 GlyphWidth;
|
|
UINTN Temp;
|
|
UINTN Temp2;
|
|
UINTN Temp3;
|
|
EFI_STATUS Status;
|
|
UINTN Row;
|
|
UINTN Col;
|
|
UINTN PromptLineNum;
|
|
UINTN OptionLineNum;
|
|
CHAR16 AdjustValue;
|
|
UINTN MaxRow;
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
Temp = SkipLine;
|
|
Temp2 = SkipLine;
|
|
Temp3 = SkipLine;
|
|
AdjustValue = 0;
|
|
PromptLineNum = 0;
|
|
OptionLineNum = 0;
|
|
MaxRow = 0;
|
|
|
|
//
|
|
// Set default color.
|
|
//
|
|
SetDisplayAttribute (MenuOption, FALSE);
|
|
|
|
//
|
|
// 1. Paint the option string.
|
|
//
|
|
Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
if (OptionString != NULL) {
|
|
if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
|
|
//
|
|
// Adjust option string for date/time opcode.
|
|
//
|
|
ProcessStringForDateTime(MenuOption, OptionString, UpdateCol);
|
|
}
|
|
|
|
Width = (UINT16) gOptionBlockWidth - 1;
|
|
Row = MenuOption->Row;
|
|
GlyphWidth = 1;
|
|
OptionLineNum = 0;
|
|
|
|
for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
|
|
if (((Temp2 == 0)) && (Row <= BottomRow)) {
|
|
if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
|
|
//
|
|
// For date/time question, it has three menu options for this qustion.
|
|
// The first/second menu options with the skip value is 0. the last one
|
|
// with skip value is 1.
|
|
//
|
|
if (MenuOption->Skip != 0) {
|
|
//
|
|
// For date/ time, print the last past (year for date and second for time)
|
|
// - 7 means skip [##/##/ for date and [##:##: for time.
|
|
//
|
|
DisplayMenuString (MenuOption,MenuOption->OptCol, Row, OutputString, Width + 1 - 7, Highlight);
|
|
} else {
|
|
//
|
|
// For date/ time, print the first and second past (year for date and second for time)
|
|
// The OutputString has a NARROW_CHAR or WIDE_CHAR at the begin of the string,
|
|
// so need to - 1 to remove it, otherwise, it will clean 1 extr char follow it.
|
|
DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, StrLen (OutputString) - 1, Highlight);
|
|
}
|
|
} else {
|
|
DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
|
|
}
|
|
OptionLineNum++;
|
|
}
|
|
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&OptionString[Index]) != 0) {
|
|
if (Temp2 == 0) {
|
|
Row++;
|
|
//
|
|
// Since the Number of lines for this menu entry may or may not be reflected accurately
|
|
// since the prompt might be 1 lines and option might be many, and vice versa, we need to do
|
|
// some testing to ensure we are keeping this in-sync.
|
|
//
|
|
// If the difference in rows is greater than or equal to the skip value, increase the skip value
|
|
//
|
|
if ((Row - MenuOption->Row) >= MenuOption->Skip) {
|
|
MenuOption->Skip++;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (OutputString);
|
|
if (Temp2 != 0) {
|
|
Temp2--;
|
|
}
|
|
}
|
|
|
|
Highlight = FALSE;
|
|
|
|
FreePool (OptionString);
|
|
}
|
|
|
|
//
|
|
// 2. Paint the description.
|
|
//
|
|
PromptWidth = GetWidth (MenuOption, &AdjustValue);
|
|
Row = MenuOption->Row;
|
|
GlyphWidth = 1;
|
|
PromptLineNum = 0;
|
|
|
|
if (MenuOption->Description == NULL || MenuOption->Description[0] == '\0') {
|
|
PrintStringAtWithWidth (BeginCol, Row, L"", PromptWidth + AdjustValue + SkipWidth);
|
|
PromptLineNum++;
|
|
} else {
|
|
for (Index = 0; GetLineByWidth (MenuOption->Description, PromptWidth, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp == 0) && (Row <= BottomRow)) {
|
|
//
|
|
// 1.Clean the start LEFT_SKIPPED_COLUMNS
|
|
//
|
|
PrintStringAtWithWidth (BeginCol, Row, L"", SkipWidth);
|
|
|
|
if (Statement->OpCode->OpCode == EFI_IFR_REF_OP && MenuOption->Col >= 2) {
|
|
//
|
|
// Print Arrow for Goto button.
|
|
//
|
|
PrintCharAt (
|
|
MenuOption->Col - 2,
|
|
Row,
|
|
GEOMETRICSHAPE_RIGHT_TRIANGLE
|
|
);
|
|
}
|
|
DisplayMenuString (MenuOption, MenuOption->Col, Row, OutputString, PromptWidth + AdjustValue, Highlight);
|
|
PromptLineNum ++;
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&MenuOption->Description[Index]) != 0) {
|
|
if (Temp == 0) {
|
|
Row++;
|
|
}
|
|
}
|
|
|
|
FreePool (OutputString);
|
|
if (Temp != 0) {
|
|
Temp--;
|
|
}
|
|
}
|
|
|
|
Highlight = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// 3. If this is a text op with secondary text information
|
|
//
|
|
if ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo != 0)) {
|
|
StringPtr = GetToken (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo, gFormData->HiiHandle);
|
|
|
|
Width = (UINT16) gOptionBlockWidth - 1;
|
|
Row = MenuOption->Row;
|
|
GlyphWidth = 1;
|
|
OptionLineNum = 0;
|
|
|
|
for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
|
|
if ((Temp3 == 0) && (Row <= BottomRow)) {
|
|
DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
|
|
OptionLineNum++;
|
|
}
|
|
//
|
|
// If there is more string to process print on the next row and increment the Skip value
|
|
//
|
|
if (StrLen (&StringPtr[Index]) != 0) {
|
|
if (Temp3 == 0) {
|
|
Row++;
|
|
//
|
|
// If the rows for text two is greater than or equal to the skip value, increase the skip value
|
|
//
|
|
if ((Row - MenuOption->Row) >= MenuOption->Skip) {
|
|
MenuOption->Skip++;
|
|
}
|
|
}
|
|
}
|
|
|
|
FreePool (OutputString);
|
|
if (Temp3 != 0) {
|
|
Temp3--;
|
|
}
|
|
}
|
|
|
|
FreePool (StringPtr);
|
|
}
|
|
|
|
//
|
|
// 4.Line number for Option string and prompt string are not equal.
|
|
// Clean the column whose line number is less.
|
|
//
|
|
if (HasOptionString(MenuOption) && (OptionLineNum != PromptLineNum)) {
|
|
Col = OptionLineNum < PromptLineNum ? MenuOption->OptCol : BeginCol;
|
|
Row = (OptionLineNum < PromptLineNum ? OptionLineNum : PromptLineNum) + MenuOption->Row;
|
|
Width = (UINT16) (OptionLineNum < PromptLineNum ? gOptionBlockWidth : PromptWidth + AdjustValue + SkipWidth);
|
|
MaxRow = (OptionLineNum < PromptLineNum ? PromptLineNum : OptionLineNum) + MenuOption->Row - 1;
|
|
|
|
while (Row <= MaxRow) {
|
|
DisplayMenuString (MenuOption, Col, Row++, L"", Width, FALSE);
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
Display menu and wait for user to select one menu option, then return it.
|
|
If AutoBoot is enabled, then if user doesn't select any option,
|
|
after period of time, it will automatically return the first menu option.
|
|
|
|
@param FormData The current form data info.
|
|
|
|
@retval EFI_SUCESSS Process the user selection success.
|
|
@retval EFI_NOT_FOUND Process option string for orderedlist/Oneof fail.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
UiDisplayMenu (
|
|
IN FORM_DISPLAY_ENGINE_FORM *FormData
|
|
)
|
|
{
|
|
UINTN SkipValue;
|
|
INTN Difference;
|
|
UINTN DistanceValue;
|
|
UINTN Row;
|
|
UINTN Col;
|
|
UINTN Temp;
|
|
UINTN Temp2;
|
|
UINTN TopRow;
|
|
UINTN BottomRow;
|
|
UINTN Index;
|
|
UINT16 Width;
|
|
CHAR16 *StringPtr;
|
|
CHAR16 *OptionString;
|
|
CHAR16 *OutputString;
|
|
CHAR16 *HelpString;
|
|
CHAR16 *HelpHeaderString;
|
|
CHAR16 *HelpBottomString;
|
|
BOOLEAN NewLine;
|
|
BOOLEAN Repaint;
|
|
BOOLEAN UpArrow;
|
|
BOOLEAN DownArrow;
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY Key;
|
|
LIST_ENTRY *Link;
|
|
LIST_ENTRY *NewPos;
|
|
LIST_ENTRY *TopOfScreen;
|
|
LIST_ENTRY *SavedListEntry;
|
|
UI_MENU_OPTION *MenuOption;
|
|
UI_MENU_OPTION *NextMenuOption;
|
|
UI_MENU_OPTION *SavedMenuOption;
|
|
UI_MENU_OPTION *PreviousMenuOption;
|
|
UI_CONTROL_FLAG ControlFlag;
|
|
UI_SCREEN_OPERATION ScreenOperation;
|
|
UINT16 DefaultId;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *Statement;
|
|
BROWSER_HOT_KEY *HotKey;
|
|
UINTN HelpPageIndex;
|
|
UINTN HelpPageCount;
|
|
UINTN RowCount;
|
|
UINTN HelpLine;
|
|
UINTN HelpHeaderLine;
|
|
UINTN HelpBottomLine;
|
|
BOOLEAN MultiHelpPage;
|
|
UINT16 EachLineWidth;
|
|
UINT16 HeaderLineWidth;
|
|
UINT16 BottomLineWidth;
|
|
EFI_STRING_ID HelpInfo;
|
|
UI_EVENT_TYPE EventType;
|
|
FORM_DISPLAY_ENGINE_STATEMENT *InitialHighlight;
|
|
BOOLEAN SkipHighLight;
|
|
|
|
EventType = UIEventNone;
|
|
Status = EFI_SUCCESS;
|
|
HelpString = NULL;
|
|
HelpHeaderString = NULL;
|
|
HelpBottomString = NULL;
|
|
OptionString = NULL;
|
|
ScreenOperation = UiNoOperation;
|
|
NewLine = TRUE;
|
|
DefaultId = 0;
|
|
HelpPageCount = 0;
|
|
HelpLine = 0;
|
|
RowCount = 0;
|
|
HelpBottomLine = 0;
|
|
HelpHeaderLine = 0;
|
|
HelpPageIndex = 0;
|
|
MultiHelpPage = FALSE;
|
|
EachLineWidth = 0;
|
|
HeaderLineWidth = 0;
|
|
BottomLineWidth = 0;
|
|
OutputString = NULL;
|
|
UpArrow = FALSE;
|
|
DownArrow = FALSE;
|
|
SkipValue = 0;
|
|
SkipHighLight = FALSE;
|
|
|
|
NextMenuOption = NULL;
|
|
PreviousMenuOption = NULL;
|
|
SavedMenuOption = NULL;
|
|
HotKey = NULL;
|
|
Repaint = TRUE;
|
|
MenuOption = NULL;
|
|
gModalSkipColumn = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 6;
|
|
InitialHighlight = gFormData->HighLightedStatement;
|
|
|
|
ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
|
|
|
|
Width = (UINT16)gOptionBlockWidth - 1;
|
|
TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT;
|
|
BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT - 1;
|
|
|
|
Row = TopRow;
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gModalSkipColumn;
|
|
} else {
|
|
Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS;
|
|
}
|
|
|
|
FindTopMenu(FormData, &TopOfScreen, &NewPos, &SkipValue);
|
|
|
|
gST->ConOut->EnableCursor (gST->ConOut, FALSE);
|
|
|
|
ControlFlag = CfInitialization;
|
|
while (TRUE) {
|
|
switch (ControlFlag) {
|
|
case CfInitialization:
|
|
if ((gOldFormEntry.HiiHandle != FormData->HiiHandle) ||
|
|
(!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))) {
|
|
//
|
|
// Clear Statement range if different formset is painted.
|
|
//
|
|
ClearLines (
|
|
gStatementDimensions.LeftColumn,
|
|
gStatementDimensions.RightColumn,
|
|
TopRow - SCROLL_ARROW_HEIGHT,
|
|
BottomRow + SCROLL_ARROW_HEIGHT,
|
|
GetFieldTextColor ()
|
|
);
|
|
|
|
}
|
|
ControlFlag = CfRepaint;
|
|
break;
|
|
|
|
case CfRepaint:
|
|
ControlFlag = CfRefreshHighLight;
|
|
|
|
if (Repaint) {
|
|
//
|
|
// Display menu
|
|
//
|
|
DownArrow = FALSE;
|
|
UpArrow = FALSE;
|
|
Row = TopRow;
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
|
|
|
|
//
|
|
// 1. Check whether need to print the arrow up.
|
|
//
|
|
if (!ValueIsScroll (TRUE, TopOfScreen)) {
|
|
UpArrow = TRUE;
|
|
}
|
|
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
|
|
} else {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
|
|
}
|
|
if (UpArrow) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
|
|
PrintCharAt (
|
|
gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
|
|
TopRow - SCROLL_ARROW_HEIGHT,
|
|
ARROW_UP
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
|
|
}
|
|
|
|
//
|
|
// 2.Paint the menu.
|
|
//
|
|
for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
|
|
MenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
MenuOption->Row = Row;
|
|
MenuOption->Col = Col;
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
MenuOption->OptCol = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth + gModalSkipColumn;
|
|
} else {
|
|
MenuOption->OptCol = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth;
|
|
}
|
|
|
|
if (MenuOption->NestInStatement) {
|
|
MenuOption->Col += SUBTITLE_INDENT;
|
|
}
|
|
|
|
//
|
|
// Save the highlight menu, will be used in CfRefreshHighLight case.
|
|
//
|
|
if (Link == NewPos) {
|
|
SavedMenuOption = MenuOption;
|
|
SkipHighLight = TRUE;
|
|
}
|
|
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
Status = DisplayOneMenu (MenuOption,
|
|
MenuOption->Col - gStatementDimensions.LeftColumn,
|
|
gStatementDimensions.LeftColumn + gModalSkipColumn,
|
|
Link == TopOfScreen ? SkipValue : 0,
|
|
BottomRow,
|
|
(BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
|
|
TRUE
|
|
);
|
|
} else {
|
|
Status = DisplayOneMenu (MenuOption,
|
|
MenuOption->Col - gStatementDimensions.LeftColumn,
|
|
gStatementDimensions.LeftColumn,
|
|
Link == TopOfScreen ? SkipValue : 0,
|
|
BottomRow,
|
|
(BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
//
|
|
// 3. Update the row info which will be used by next menu.
|
|
//
|
|
if (Link == TopOfScreen) {
|
|
Row += MenuOption->Skip - SkipValue;
|
|
} else {
|
|
Row += MenuOption->Skip;
|
|
}
|
|
|
|
if (Row > BottomRow) {
|
|
if (!ValueIsScroll (FALSE, Link)) {
|
|
DownArrow = TRUE;
|
|
}
|
|
|
|
Row = BottomRow + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// 3. Menus in this form may not cover all form, clean the remain field.
|
|
//
|
|
while (Row <= BottomRow) {
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, Row++, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
|
|
} else {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn, Row++, L"", gStatementDimensions.RightColumn - gHelpBlockWidth - gStatementDimensions.LeftColumn);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 4. Print the down arrow row.
|
|
//
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * + gModalSkipColumn);
|
|
} else {
|
|
PrintStringAtWithWidth(gStatementDimensions.LeftColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
|
|
}
|
|
if (DownArrow) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
|
|
PrintCharAt (
|
|
gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
|
|
BottomRow + SCROLL_ARROW_HEIGHT,
|
|
ARROW_DOWN
|
|
);
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
|
|
}
|
|
|
|
MenuOption = NULL;
|
|
}
|
|
break;
|
|
|
|
case CfRefreshHighLight:
|
|
|
|
//
|
|
// MenuOption: Last menu option that need to remove hilight
|
|
// MenuOption is set to NULL in Repaint
|
|
// NewPos: Current menu option that need to hilight
|
|
//
|
|
ControlFlag = CfUpdateHelpString;
|
|
|
|
if (SkipHighLight) {
|
|
MenuOption = SavedMenuOption;
|
|
SkipHighLight = FALSE;
|
|
UpdateHighlightMenuInfo (MenuOption, TopOfScreen == &MenuOption->Link ? SkipValue : 0);
|
|
break;
|
|
}
|
|
|
|
if (IsListEmpty (&gMenuOption)) {
|
|
//
|
|
// No menu option, just update the hotkey filed.
|
|
//
|
|
RefreshKeyHelp(gFormData, NULL, FALSE);
|
|
break;
|
|
}
|
|
|
|
if (MenuOption != NULL && TopOfScreen == &MenuOption->Link) {
|
|
Temp = SkipValue;
|
|
} else {
|
|
Temp = 0;
|
|
}
|
|
if (NewPos == TopOfScreen) {
|
|
Temp2 = SkipValue;
|
|
} else {
|
|
Temp2 = 0;
|
|
}
|
|
|
|
if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
|
|
if (MenuOption != NULL) {
|
|
//
|
|
// Remove the old highlight menu.
|
|
//
|
|
Status = DisplayOneMenu (MenuOption,
|
|
MenuOption->Col - gStatementDimensions.LeftColumn,
|
|
gStatementDimensions.LeftColumn,
|
|
Temp,
|
|
BottomRow,
|
|
FALSE,
|
|
FALSE
|
|
);
|
|
}
|
|
|
|
//
|
|
// This is the current selected statement
|
|
//
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
Statement = MenuOption->ThisTag;
|
|
|
|
UpdateHighlightMenuInfo (MenuOption, Temp2);
|
|
|
|
if (!IsSelectable (MenuOption)) {
|
|
break;
|
|
}
|
|
|
|
Status = DisplayOneMenu (MenuOption,
|
|
MenuOption->Col - gStatementDimensions.LeftColumn,
|
|
gStatementDimensions.LeftColumn,
|
|
Temp2,
|
|
BottomRow,
|
|
TRUE,
|
|
FALSE
|
|
);
|
|
}
|
|
break;
|
|
|
|
case CfUpdateHelpString:
|
|
ControlFlag = CfPrepareToReadKey;
|
|
if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// NewLine means only update highlight menu (remove old highlight and highlith
|
|
// the new one), not need to full repain the form.
|
|
//
|
|
if (Repaint || NewLine) {
|
|
if (IsListEmpty (&gMenuOption)) {
|
|
//
|
|
// Don't print anything if no mwnu option.
|
|
//
|
|
StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
|
|
} else {
|
|
//
|
|
// Don't print anything if it is a NULL help token
|
|
//
|
|
ASSERT(MenuOption != NULL);
|
|
HelpInfo = ((EFI_IFR_STATEMENT_HEADER *) ((CHAR8 *)MenuOption->ThisTag->OpCode + sizeof (EFI_IFR_OP_HEADER)))->Help;
|
|
if (HelpInfo == 0 || !IsSelectable (MenuOption)) {
|
|
StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
|
|
} else {
|
|
StringPtr = GetToken (HelpInfo, gFormData->HiiHandle);
|
|
}
|
|
}
|
|
|
|
RowCount = BottomRow - TopRow + 1;
|
|
HelpPageIndex = 0;
|
|
//
|
|
// 1.Calculate how many line the help string need to print.
|
|
//
|
|
if (HelpString != NULL) {
|
|
FreePool (HelpString);
|
|
HelpString = NULL;
|
|
}
|
|
HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount);
|
|
FreePool (StringPtr);
|
|
|
|
if (HelpLine > RowCount) {
|
|
MultiHelpPage = TRUE;
|
|
StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle);
|
|
if (HelpHeaderString != NULL) {
|
|
FreePool (HelpHeaderString);
|
|
HelpHeaderString = NULL;
|
|
}
|
|
HelpHeaderLine = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, 0);
|
|
FreePool (StringPtr);
|
|
StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle);
|
|
if (HelpBottomString != NULL) {
|
|
FreePool (HelpBottomString);
|
|
HelpBottomString = NULL;
|
|
}
|
|
HelpBottomLine = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, 0);
|
|
FreePool (StringPtr);
|
|
//
|
|
// Calculate the help page count.
|
|
//
|
|
if (HelpLine > 2 * RowCount - 2) {
|
|
HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1;
|
|
if ((HelpLine - RowCount + 1) % (RowCount - 2) > 1) {
|
|
HelpPageCount += 1;
|
|
}
|
|
} else {
|
|
HelpPageCount = 2;
|
|
}
|
|
} else {
|
|
MultiHelpPage = FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether need to show the 'More(U/u)' at the begin.
|
|
// Base on current direct info, here shows aligned to the right side of the column.
|
|
// If the direction is multi line and aligned to right side may have problem, so
|
|
// add ASSERT code here.
|
|
//
|
|
if (HelpPageIndex > 0) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
|
|
for (Index = 0; Index < HelpHeaderLine; Index++) {
|
|
ASSERT (HelpHeaderLine == 1);
|
|
ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1));
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
gEmptyString,
|
|
gHelpBlockWidth
|
|
);
|
|
PrintStringAt (
|
|
gStatementDimensions.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1,
|
|
Index + TopRow,
|
|
&HelpHeaderString[Index * HeaderLineWidth]
|
|
);
|
|
}
|
|
}
|
|
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetHelpTextColor ());
|
|
//
|
|
// Print the help string info.
|
|
//
|
|
if (!MultiHelpPage) {
|
|
for (Index = 0; Index < HelpLine; Index++) {
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
&HelpString[Index * EachLineWidth],
|
|
gHelpBlockWidth
|
|
);
|
|
}
|
|
for (; Index < RowCount; Index ++) {
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
gEmptyString,
|
|
gHelpBlockWidth
|
|
);
|
|
}
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
|
|
} else {
|
|
if (HelpPageIndex == 0) {
|
|
for (Index = 0; Index < RowCount - HelpBottomLine; Index++) {
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow,
|
|
&HelpString[Index * EachLineWidth],
|
|
gHelpBlockWidth
|
|
);
|
|
}
|
|
} else {
|
|
for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) &&
|
|
(Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) {
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow + HelpHeaderLine,
|
|
&HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth],
|
|
gHelpBlockWidth
|
|
);
|
|
}
|
|
if (HelpPageIndex == HelpPageCount - 1) {
|
|
for (; Index < RowCount - HelpHeaderLine; Index ++) {
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
Index + TopRow + HelpHeaderLine,
|
|
gEmptyString,
|
|
gHelpBlockWidth
|
|
);
|
|
}
|
|
gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check whether need to print the 'More(D/d)' at the bottom.
|
|
// Base on current direct info, here shows aligned to the right side of the column.
|
|
// If the direction is multi line and aligned to right side may have problem, so
|
|
// add ASSERT code here.
|
|
//
|
|
if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) {
|
|
gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
|
|
for (Index = 0; Index < HelpBottomLine; Index++) {
|
|
ASSERT (HelpBottomLine == 1);
|
|
ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1));
|
|
PrintStringAtWithWidth (
|
|
gStatementDimensions.RightColumn - gHelpBlockWidth,
|
|
BottomRow + Index - HelpBottomLine + 1,
|
|
gEmptyString,
|
|
gHelpBlockWidth
|
|
);
|
|
PrintStringAt (
|
|
gStatementDimensions.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1,
|
|
BottomRow + Index - HelpBottomLine + 1,
|
|
&HelpBottomString[Index * BottomLineWidth]
|
|
);
|
|
}
|
|
}
|
|
//
|
|
// Reset this flag every time we finish using it.
|
|
//
|
|
Repaint = FALSE;
|
|
NewLine = FALSE;
|
|
break;
|
|
|
|
case CfPrepareToReadKey:
|
|
ControlFlag = CfReadKey;
|
|
ScreenOperation = UiNoOperation;
|
|
break;
|
|
|
|
case CfReadKey:
|
|
ControlFlag = CfScreenOperation;
|
|
|
|
//
|
|
// Wait for user's selection
|
|
//
|
|
while (TRUE) {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
if (!EFI_ERROR (Status)) {
|
|
EventType = UIEventKey;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we encounter error, continue to read another key in.
|
|
//
|
|
if (Status != EFI_NOT_READY) {
|
|
continue;
|
|
}
|
|
|
|
EventType = UiWaitForEvent(gST->ConIn->WaitForKey);
|
|
if (EventType == UIEventKey) {
|
|
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (EventType == UIEventDriver) {
|
|
gUserInput->Action = BROWSER_ACTION_NONE;
|
|
ControlFlag = CfExit;
|
|
break;
|
|
}
|
|
|
|
if (EventType == UIEventTimeOut) {
|
|
gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
|
|
ControlFlag = CfExit;
|
|
break;
|
|
}
|
|
|
|
switch (Key.UnicodeChar) {
|
|
case CHAR_CARRIAGE_RETURN:
|
|
if(MenuOption == NULL || MenuOption->GrayOut || MenuOption->ReadOnly) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
ScreenOperation = UiSelect;
|
|
gDirection = 0;
|
|
break;
|
|
|
|
//
|
|
// We will push the adjustment of these numeric values directly to the input handler
|
|
// NOTE: we won't handle manual input numeric
|
|
//
|
|
case '+':
|
|
case '-':
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiReset
|
|
// ignore the selection and go back to reading keys.
|
|
//
|
|
ASSERT(MenuOption != NULL);
|
|
if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
Statement = MenuOption->ThisTag;
|
|
if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP)
|
|
|| (Statement->OpCode->OpCode == EFI_IFR_TIME_OP)
|
|
|| ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (GetFieldFromNum(Statement->OpCode) != 0))
|
|
){
|
|
if (Key.UnicodeChar == '+') {
|
|
gDirection = SCAN_RIGHT;
|
|
} else {
|
|
gDirection = SCAN_LEFT;
|
|
}
|
|
|
|
Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
|
|
if (OptionString != NULL) {
|
|
FreePool (OptionString);
|
|
}
|
|
if (EFI_ERROR (Status)) {
|
|
//
|
|
// Repaint to clear possible error prompt pop-up
|
|
//
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
} else {
|
|
ControlFlag = CfExit;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case '^':
|
|
ScreenOperation = UiUp;
|
|
break;
|
|
|
|
case 'V':
|
|
case 'v':
|
|
ScreenOperation = UiDown;
|
|
break;
|
|
|
|
case ' ':
|
|
if(IsListEmpty (&gMenuOption)) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
|
|
ASSERT(MenuOption != NULL);
|
|
if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
|
|
ScreenOperation = UiSelect;
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
case 'd':
|
|
if (!MultiHelpPage) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
ControlFlag = CfUpdateHelpString;
|
|
HelpPageIndex = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1;
|
|
break;
|
|
|
|
case 'U':
|
|
case 'u':
|
|
if (!MultiHelpPage) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
ControlFlag = CfUpdateHelpString;
|
|
HelpPageIndex = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0;
|
|
break;
|
|
|
|
case CHAR_NULL:
|
|
for (Index = 0; Index < mScanCodeNumber; Index++) {
|
|
if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
|
|
ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (((FormData->Attribute & HII_DISPLAY_MODAL) != 0) && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) {
|
|
//
|
|
// ModalForm has no ESC key and Hot Key.
|
|
//
|
|
ControlFlag = CfReadKey;
|
|
} else if (Index == mScanCodeNumber) {
|
|
//
|
|
// Check whether Key matches the registered hot key.
|
|
//
|
|
HotKey = NULL;
|
|
HotKey = GetHotKeyFromRegisterList (&Key);
|
|
if (HotKey != NULL) {
|
|
ScreenOperation = UiHotKey;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case CfScreenOperation:
|
|
if ((ScreenOperation != UiReset) && (ScreenOperation != UiHotKey)) {
|
|
//
|
|
// If the screen has no menu items, and the user didn't select UiReset or UiHotKey
|
|
// ignore the selection and go back to reading keys.
|
|
//
|
|
if (IsListEmpty (&gMenuOption)) {
|
|
ControlFlag = CfReadKey;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (Index = 0;
|
|
Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
|
|
Index++
|
|
) {
|
|
if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
|
|
ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiSelect:
|
|
ControlFlag = CfRepaint;
|
|
|
|
ASSERT(MenuOption != NULL);
|
|
Statement = MenuOption->ThisTag;
|
|
if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
|
|
break;
|
|
}
|
|
|
|
switch (Statement->OpCode->OpCode) {
|
|
case EFI_IFR_REF_OP:
|
|
case EFI_IFR_ACTION_OP:
|
|
case EFI_IFR_RESET_BUTTON_OP:
|
|
ControlFlag = CfExit;
|
|
break;
|
|
|
|
default:
|
|
//
|
|
// Editable Questions: oneof, ordered list, checkbox, numeric, string, password
|
|
//
|
|
RefreshKeyHelp (gFormData, Statement, TRUE);
|
|
Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
|
|
|
|
if (OptionString != NULL) {
|
|
FreePool (OptionString);
|
|
}
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
RefreshKeyHelp (gFormData, Statement, FALSE);
|
|
break;
|
|
} else {
|
|
ControlFlag = CfExit;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiReset:
|
|
//
|
|
// We come here when someone press ESC
|
|
// If the policy is not exit front page when user press ESC, process here.
|
|
//
|
|
if (!FormExitPolicy()) {
|
|
Repaint = TRUE;
|
|
NewLine = TRUE;
|
|
ControlFlag = CfRepaint;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// When user press ESC, it will try to show another menu, should clean the gSequence info.
|
|
//
|
|
if (gSequence != 0) {
|
|
gSequence = 0;
|
|
}
|
|
|
|
gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
|
|
ControlFlag = CfExit;
|
|
break;
|
|
|
|
case CfUiHotKey:
|
|
ControlFlag = CfRepaint;
|
|
|
|
ASSERT (HotKey != NULL);
|
|
gUserInput->Action = HotKey->Action;
|
|
ControlFlag = CfExit;
|
|
break;
|
|
|
|
case CfUiLeft:
|
|
ControlFlag = CfRepaint;
|
|
ASSERT(MenuOption != NULL);
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
|
|
if (MenuOption->Sequence != 0) {
|
|
//
|
|
// In the middle or tail of the Date/Time op-code set, go left.
|
|
//
|
|
ASSERT(NewPos != NULL);
|
|
NewPos = NewPos->BackLink;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiRight:
|
|
ControlFlag = CfRepaint;
|
|
ASSERT(MenuOption != NULL);
|
|
if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
|
|
if (MenuOption->Sequence != 2) {
|
|
//
|
|
// In the middle or tail of the Date/Time op-code set, go left.
|
|
//
|
|
ASSERT(NewPos != NULL);
|
|
NewPos = NewPos->ForwardLink;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CfUiUp:
|
|
ControlFlag = CfRepaint;
|
|
NewLine = TRUE;
|
|
|
|
SavedListEntry = NewPos;
|
|
ASSERT(NewPos != NULL);
|
|
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
ASSERT (MenuOption != NULL);
|
|
|
|
//
|
|
// Adjust Date/Time position before we advance forward.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
|
|
NewPos = NewPos->BackLink;
|
|
//
|
|
// Find next selectable menu or the first menu beyond current form.
|
|
//
|
|
Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow, FALSE);
|
|
if (Difference < 0) {
|
|
//
|
|
// We hit the begining MenuOption that can be focused
|
|
// so we simply scroll to the top.
|
|
//
|
|
Repaint = TRUE;
|
|
if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
|
|
TopOfScreen = gMenuOption.ForwardLink;
|
|
NewPos = SavedListEntry;
|
|
SkipValue = 0;
|
|
} else {
|
|
//
|
|
// Scroll up to the last page when we have arrived at top page.
|
|
//
|
|
TopOfScreen = FindTopOfScreenMenu (gMenuOption.BackLink, BottomRow - TopRow, &SkipValue);
|
|
NewPos = gMenuOption.BackLink;
|
|
MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow, TRUE);
|
|
}
|
|
} else {
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
|
|
if (MenuOption->Row < TopRow + Difference + NextMenuOption->Skip) {
|
|
//
|
|
// Previous focus MenuOption is above the TopOfScreen, so we need to scroll
|
|
//
|
|
TopOfScreen = NewPos;
|
|
Repaint = TRUE;
|
|
SkipValue = 0;
|
|
}
|
|
|
|
//
|
|
// Check whether new highlight menu is selectable, if not, keep highlight on the old one.
|
|
//
|
|
// BottomRow - TopRow + 1 means the total rows current forms supported.
|
|
// Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
|
|
// and new top menu. New top menu will all shows in next form, but last highlight menu
|
|
// may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
|
|
// last highlight menu.
|
|
//
|
|
if (!IsSelectable(NextMenuOption) && IsSelectable(MenuOption) &&
|
|
(BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
|
|
NewPos = SavedListEntry;
|
|
}
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, FALSE);
|
|
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
|
|
case CfUiPageUp:
|
|
//
|
|
// SkipValue means lines is skipped when show the top menu option.
|
|
//
|
|
ControlFlag = CfRepaint;
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
|
|
Link = TopOfScreen;
|
|
//
|
|
// First minus the menu of the top screen, it's value is SkipValue.
|
|
//
|
|
if (SkipValue >= BottomRow - TopRow + 1) {
|
|
//
|
|
// SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
|
|
// form of options to be show, so just update the SkipValue to show the next
|
|
// parts of options.
|
|
//
|
|
SkipValue -= BottomRow - TopRow + 1;
|
|
NewPos = TopOfScreen;
|
|
break;
|
|
} else {
|
|
Index = (BottomRow + 1) - SkipValue - TopRow;
|
|
}
|
|
|
|
TopOfScreen = FindTopOfScreenMenu(TopOfScreen, Index, &SkipValue);
|
|
NewPos = TopOfScreen;
|
|
MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, FALSE);
|
|
|
|
UpdateStatusBar (INPUT_ERROR, FALSE);
|
|
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
// Don't do this when we are already in the first page.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
|
|
case CfUiPageDown:
|
|
//
|
|
// SkipValue means lines is skipped when show the top menu option.
|
|
//
|
|
ControlFlag = CfRepaint;
|
|
NewLine = TRUE;
|
|
Repaint = TRUE;
|
|
|
|
Link = TopOfScreen;
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index = TopRow + NextMenuOption->Skip - SkipValue;
|
|
//
|
|
// Count to the menu option which will show at the top of the next form.
|
|
//
|
|
while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) {
|
|
Link = Link->ForwardLink;
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (Link);
|
|
Index = Index + NextMenuOption->Skip;
|
|
}
|
|
|
|
if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) {
|
|
//
|
|
// Highlight on the last menu which can be highlight.
|
|
//
|
|
Repaint = FALSE;
|
|
MoveToNextStatement (TRUE, &Link, Index - TopRow, TRUE);
|
|
} else {
|
|
//
|
|
// Calculate the skip line for top of screen menu.
|
|
//
|
|
if (Link == TopOfScreen) {
|
|
//
|
|
// The top of screen menu option occupies the entire form.
|
|
//
|
|
SkipValue += BottomRow - TopRow + 1;
|
|
} else {
|
|
SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1));
|
|
}
|
|
TopOfScreen = Link;
|
|
MenuOption = NULL;
|
|
//
|
|
// Move to the Next selectable menu.
|
|
//
|
|
MoveToNextStatement (FALSE, &Link, BottomRow - TopRow, TRUE);
|
|
}
|
|
|
|
//
|
|
// Save the menu as the next highlight menu.
|
|
//
|
|
NewPos = Link;
|
|
|
|
UpdateStatusBar (INPUT_ERROR, FALSE);
|
|
|
|
//
|
|
// If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
|
|
// Don't do this when we are already in the last page.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
|
|
case CfUiDown:
|
|
//
|
|
// SkipValue means lines is skipped when show the top menu option.
|
|
// NewPos points to the menu which is highlighted now.
|
|
//
|
|
ControlFlag = CfRepaint;
|
|
NewLine = TRUE;
|
|
|
|
if (NewPos == TopOfScreen) {
|
|
Temp2 = SkipValue;
|
|
} else {
|
|
Temp2 = 0;
|
|
}
|
|
|
|
SavedListEntry = NewPos;
|
|
//
|
|
// Since the behavior of hitting the down arrow on a Date/Time op-code is intended
|
|
// to be one that progresses to the next set of op-codes, we need to advance to the last
|
|
// Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
|
|
// checking can be done. The only other logic we need to introduce is that if a Date/Time
|
|
// op-code is the last entry in the menu, we need to rewind back to the first op-code of
|
|
// the Date/Time op-code.
|
|
//
|
|
AdjustDateAndTimePosition (FALSE, &NewPos);
|
|
|
|
MenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
NewPos = NewPos->ForwardLink;
|
|
//
|
|
// Find the next selectable menu.
|
|
//
|
|
if (MenuOption->Row + MenuOption->Skip - Temp2 > BottomRow + 1) {
|
|
if (gMenuOption.ForwardLink == NewPos || &gMenuOption == NewPos) {
|
|
Difference = -1;
|
|
} else {
|
|
Difference = 0;
|
|
}
|
|
} else {
|
|
Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow + 1 - (MenuOption->Row + MenuOption->Skip - Temp2), FALSE);
|
|
}
|
|
if (Difference < 0) {
|
|
//
|
|
// Scroll to the first page.
|
|
//
|
|
if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
|
|
TopOfScreen = gMenuOption.ForwardLink;
|
|
Repaint = TRUE;
|
|
MenuOption = NULL;
|
|
} else {
|
|
MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
|
|
}
|
|
NewPos = gMenuOption.ForwardLink;
|
|
MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, TRUE);
|
|
|
|
SkipValue = 0;
|
|
//
|
|
// If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Get next selected menu info.
|
|
//
|
|
AdjustDateAndTimePosition (FALSE, &NewPos);
|
|
NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
|
|
if (NextMenuOption->Row == 0) {
|
|
UpdateOptionSkipLines (NextMenuOption);
|
|
}
|
|
|
|
//
|
|
// Calculate new highlight menu end row.
|
|
//
|
|
Temp = (MenuOption->Row + MenuOption->Skip - Temp2) + Difference + NextMenuOption->Skip - 1;
|
|
if (Temp > BottomRow) {
|
|
//
|
|
// Get the top screen menu info.
|
|
//
|
|
AdjustDateAndTimePosition (FALSE, &TopOfScreen);
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
|
|
//
|
|
// Current Top screen menu occupy (SavedMenuOption->Skip - SkipValue) rows.
|
|
// Full shows the new selected menu need to skip (Temp - BottomRow - 1) rows.
|
|
//
|
|
if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) {
|
|
//
|
|
// Skip the top op-code
|
|
//
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
DistanceValue = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue);
|
|
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
|
|
//
|
|
// If we have a remainder, skip that many more op-codes until we drain the remainder
|
|
// Special case is the selected highlight menu has more than one form of menus.
|
|
//
|
|
while (DistanceValue >= SavedMenuOption->Skip && TopOfScreen != NewPos) {
|
|
//
|
|
// Since the Difference is greater than or equal to this op-code's skip value, skip it
|
|
//
|
|
DistanceValue = DistanceValue - (INTN) SavedMenuOption->Skip;
|
|
TopOfScreen = TopOfScreen->ForwardLink;
|
|
SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
|
|
}
|
|
//
|
|
// Since we will act on this op-code in the next routine, and increment the
|
|
// SkipValue, set the skips to one less than what is required.
|
|
//
|
|
if (TopOfScreen != NewPos) {
|
|
SkipValue = DistanceValue;
|
|
} else {
|
|
SkipValue = 0;
|
|
}
|
|
} else {
|
|
//
|
|
// Since we will act on this op-code in the next routine, and increment the
|
|
// SkipValue, set the skips to one less than what is required.
|
|
//
|
|
SkipValue += Temp - BottomRow;
|
|
}
|
|
Repaint = TRUE;
|
|
} else if (!IsSelectable (NextMenuOption)) {
|
|
//
|
|
// Continue to go down until scroll to next page or the selectable option is found.
|
|
//
|
|
ScreenOperation = UiDown;
|
|
ControlFlag = CfScreenOperation;
|
|
break;
|
|
}
|
|
|
|
MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
|
|
|
|
//
|
|
// Check whether new highlight menu is selectable, if not, keep highlight on the old one.
|
|
//
|
|
// BottomRow - TopRow + 1 means the total rows current forms supported.
|
|
// Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
|
|
// and new top menu. New top menu will all shows in next form, but last highlight menu
|
|
// may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
|
|
// last highlight menu.
|
|
//
|
|
if (!IsSelectable (NextMenuOption) && IsSelectable (MenuOption) &&
|
|
(BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
|
|
NewPos = SavedListEntry;
|
|
}
|
|
|
|
UpdateStatusBar (INPUT_ERROR, FALSE);
|
|
|
|
//
|
|
// If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
|
|
//
|
|
AdjustDateAndTimePosition (TRUE, &TopOfScreen);
|
|
AdjustDateAndTimePosition (TRUE, &NewPos);
|
|
break;
|
|
|
|
case CfUiNoOperation:
|
|
ControlFlag = CfRepaint;
|
|
break;
|
|
|
|
case CfExit:
|
|
gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
|
|
if (HelpString != NULL) {
|
|
FreePool (HelpString);
|
|
}
|
|
if (HelpHeaderString != NULL) {
|
|
FreePool (HelpHeaderString);
|
|
}
|
|
if (HelpBottomString != NULL) {
|
|
FreePool (HelpBottomString);
|
|
}
|
|
return EFI_SUCCESS;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
|
|
Base on the browser status info to show an pop up message.
|
|
|
|
**/
|
|
VOID
|
|
BrowserStatusProcess (
|
|
VOID
|
|
)
|
|
{
|
|
CHAR16 *ErrorInfo;
|
|
EFI_INPUT_KEY Key;
|
|
EFI_EVENT WaitList[2];
|
|
EFI_EVENT RefreshIntervalEvent;
|
|
EFI_EVENT TimeOutEvent;
|
|
UINT8 TimeOut;
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
WARNING_IF_CONTEXT EventContext;
|
|
EFI_IFR_OP_HEADER *OpCodeBuf;
|
|
EFI_STRING_ID StringToken;
|
|
CHAR16 DiscardChange;
|
|
CHAR16 JumpToFormSet;
|
|
CHAR16 *PrintString;
|
|
|
|
if (gFormData->BrowserStatus == BROWSER_SUCCESS) {
|
|
return;
|
|
}
|
|
|
|
StringToken = 0;
|
|
TimeOutEvent = NULL;
|
|
RefreshIntervalEvent = NULL;
|
|
OpCodeBuf = NULL;
|
|
if (gFormData->HighLightedStatement != NULL) {
|
|
OpCodeBuf = gFormData->HighLightedStatement->OpCode;
|
|
}
|
|
|
|
if (gFormData->BrowserStatus == (BROWSER_WARNING_IF)) {
|
|
ASSERT (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_WARNING_IF_OP);
|
|
|
|
TimeOut = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->TimeOut;
|
|
StringToken = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->Warning;
|
|
} else {
|
|
TimeOut = 0;
|
|
if ((gFormData->BrowserStatus == (BROWSER_NO_SUBMIT_IF)) &&
|
|
(OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_NO_SUBMIT_IF_OP)) {
|
|
StringToken = ((EFI_IFR_NO_SUBMIT_IF *) OpCodeBuf)->Error;
|
|
} else if ((gFormData->BrowserStatus == (BROWSER_INCONSISTENT_IF)) &&
|
|
(OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_INCONSISTENT_IF_OP)) {
|
|
StringToken = ((EFI_IFR_INCONSISTENT_IF *) OpCodeBuf)->Error;
|
|
}
|
|
}
|
|
|
|
if (StringToken != 0) {
|
|
ErrorInfo = GetToken (StringToken, gFormData->HiiHandle);
|
|
} else if (gFormData->ErrorString != NULL) {
|
|
//
|
|
// Only used to compatible with old setup browser.
|
|
// Not use this field in new browser core.
|
|
//
|
|
ErrorInfo = gFormData->ErrorString;
|
|
} else {
|
|
switch (gFormData->BrowserStatus) {
|
|
case BROWSER_SUBMIT_FAIL:
|
|
ErrorInfo = gSaveFailed;
|
|
break;
|
|
|
|
case BROWSER_FORM_NOT_FOUND:
|
|
ErrorInfo = gFormNotFound;
|
|
break;
|
|
|
|
case BROWSER_FORM_SUPPRESS:
|
|
ErrorInfo = gFormSuppress;
|
|
break;
|
|
|
|
case BROWSER_PROTOCOL_NOT_FOUND:
|
|
ErrorInfo = gProtocolNotFound;
|
|
break;
|
|
|
|
case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
|
|
ErrorInfo = gNoSubmitIfFailed;
|
|
break;
|
|
|
|
default:
|
|
ErrorInfo = gBrwoserError;
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (gFormData->BrowserStatus) {
|
|
case BROWSER_SUBMIT_FAIL:
|
|
case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
|
|
ASSERT (gUserInput != NULL);
|
|
if (gFormData->BrowserStatus == (BROWSER_SUBMIT_FAIL)) {
|
|
PrintString = gSaveProcess;
|
|
JumpToFormSet = gJumpToFormSet[0];
|
|
} else {
|
|
PrintString = gSaveNoSubmitProcess;
|
|
JumpToFormSet = gCheckError[0];
|
|
}
|
|
DiscardChange = gDiscardChange[0];
|
|
|
|
do {
|
|
CreateDialog (&Key, gEmptyString, ErrorInfo, PrintString, gEmptyString, NULL);
|
|
} while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (DiscardChange | UPPER_LOWER_CASE_OFFSET)) &&
|
|
((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (JumpToFormSet | UPPER_LOWER_CASE_OFFSET)));
|
|
|
|
if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (DiscardChange | UPPER_LOWER_CASE_OFFSET)) {
|
|
gUserInput->Action = BROWSER_ACTION_DISCARD;
|
|
} else {
|
|
gUserInput->Action = BROWSER_ACTION_GOTO;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (TimeOut == 0) {
|
|
do {
|
|
CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL);
|
|
} while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
|
|
} else {
|
|
Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, EmptyEventProcess, NULL, &TimeOutEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
EventContext.SyncEvent = TimeOutEvent;
|
|
EventContext.TimeOut = &TimeOut;
|
|
EventContext.ErrorInfo = ErrorInfo;
|
|
|
|
Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, RefreshTimeOutProcess, &EventContext, &RefreshIntervalEvent);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
//
|
|
// Show the dialog first to avoid long time not reaction.
|
|
//
|
|
gBS->SignalEvent (RefreshIntervalEvent);
|
|
|
|
Status = gBS->SetTimer (RefreshIntervalEvent, TimerPeriodic, ONE_SECOND);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
while (TRUE) {
|
|
Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
|
|
if (!EFI_ERROR (Status) && Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
|
|
break;
|
|
}
|
|
|
|
if (Status != EFI_NOT_READY) {
|
|
continue;
|
|
}
|
|
|
|
WaitList[0] = TimeOutEvent;
|
|
WaitList[1] = gST->ConIn->WaitForKey;
|
|
|
|
Status = gBS->WaitForEvent (2, WaitList, &Index);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
if (Index == 0) {
|
|
//
|
|
// Timeout occur, close the hoot time out event.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
|
|
gBS->CloseEvent (TimeOutEvent);
|
|
gBS->CloseEvent (RefreshIntervalEvent);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (StringToken != 0) {
|
|
FreePool (ErrorInfo);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Display one form, and return user input.
|
|
|
|
@param FormData Form Data to be shown.
|
|
@param UserInputData User input data.
|
|
|
|
@retval EFI_SUCCESS 1.Form Data is shown, and user input is got.
|
|
2.Error info has show and return.
|
|
@retval EFI_INVALID_PARAMETER The input screen dimension is not valid
|
|
@retval EFI_NOT_FOUND New form data has some error.
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
FormDisplay (
|
|
IN FORM_DISPLAY_ENGINE_FORM *FormData,
|
|
OUT USER_INPUT *UserInputData
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
|
|
ASSERT (FormData != NULL);
|
|
if (FormData == NULL) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
gUserInput = UserInputData;
|
|
gFormData = FormData;
|
|
|
|
//
|
|
// Process the status info first.
|
|
//
|
|
BrowserStatusProcess();
|
|
if (gFormData->BrowserStatus != BROWSER_SUCCESS) {
|
|
//
|
|
// gFormData->BrowserStatus != BROWSER_SUCCESS, means only need to print the error info, return here.
|
|
//
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
Status = DisplayPageFrame (FormData, &gStatementDimensions);
|
|
if (EFI_ERROR (Status)) {
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Global Widths should be initialized before any MenuOption creation
|
|
// or the GetWidth() used in UiAddMenuOption() will return incorrect value.
|
|
//
|
|
//
|
|
// Left right
|
|
// |<-.->|<-.........->|<- .........->|<-...........->|
|
|
// Skip Prompt Option Help
|
|
//
|
|
gOptionBlockWidth = (CHAR16) ((gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 3) + 1;
|
|
gHelpBlockWidth = (CHAR16) (gOptionBlockWidth - 1 - LEFT_SKIPPED_COLUMNS);
|
|
gPromptBlockWidth = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gOptionBlockWidth - 1) - 1);
|
|
|
|
ConvertStatementToMenu();
|
|
|
|
//
|
|
// Check whether layout is changed.
|
|
//
|
|
if (mIsFirstForm
|
|
|| (gOldFormEntry.HiiHandle != FormData->HiiHandle)
|
|
|| (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))
|
|
|| (gOldFormEntry.FormId != FormData->FormId)) {
|
|
mStatementLayoutIsChanged = TRUE;
|
|
} else {
|
|
mStatementLayoutIsChanged = FALSE;
|
|
}
|
|
|
|
Status = UiDisplayMenu(FormData);
|
|
|
|
//
|
|
// Backup last form info.
|
|
//
|
|
mIsFirstForm = FALSE;
|
|
gOldFormEntry.HiiHandle = FormData->HiiHandle;
|
|
CopyGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid);
|
|
gOldFormEntry.FormId = FormData->FormId;
|
|
|
|
return Status;
|
|
}
|
|
|
|
/**
|
|
Clear Screen to the initial state.
|
|
**/
|
|
VOID
|
|
EFIAPI
|
|
DriverClearDisplayPage (
|
|
VOID
|
|
)
|
|
{
|
|
ClearDisplayPage ();
|
|
mIsFirstForm = TRUE;
|
|
}
|
|
|
|
/**
|
|
Set Buffer to Value for Size bytes.
|
|
|
|
@param Buffer Memory to set.
|
|
@param Size Number of bytes to set
|
|
@param Value Value of the set operation.
|
|
|
|
**/
|
|
VOID
|
|
SetUnicodeMem (
|
|
IN VOID *Buffer,
|
|
IN UINTN Size,
|
|
IN CHAR16 Value
|
|
)
|
|
{
|
|
CHAR16 *Ptr;
|
|
|
|
Ptr = Buffer;
|
|
while ((Size--) != 0) {
|
|
*(Ptr++) = Value;
|
|
}
|
|
}
|
|
|
|
/**
|
|
Initialize Setup Browser driver.
|
|
|
|
@param ImageHandle The image handle.
|
|
@param SystemTable The system table.
|
|
|
|
@retval EFI_SUCCESS The Setup Browser module is initialized correctly..
|
|
@return Other value if failed to initialize the Setup Browser module.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
InitializeDisplayEngine (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_INPUT_KEY HotKey;
|
|
EFI_STRING NewString;
|
|
EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;
|
|
|
|
//
|
|
// Publish our HII data
|
|
//
|
|
gHiiHandle = HiiAddPackages (
|
|
&gDisplayEngineGuid,
|
|
ImageHandle,
|
|
DisplayEngineStrings,
|
|
NULL
|
|
);
|
|
ASSERT (gHiiHandle != NULL);
|
|
|
|
//
|
|
// Install Form Display protocol
|
|
//
|
|
Status = gBS->InstallProtocolInterface (
|
|
&mPrivateData.Handle,
|
|
&gEdkiiFormDisplayEngineProtocolGuid,
|
|
EFI_NATIVE_INTERFACE,
|
|
&mPrivateData.FromDisplayProt
|
|
);
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
InitializeDisplayStrings();
|
|
|
|
ZeroMem (&gHighligthMenuInfo, sizeof (gHighligthMenuInfo));
|
|
ZeroMem (&gOldFormEntry, sizeof (gOldFormEntry));
|
|
|
|
//
|
|
// Use BrowserEx2 protocol to register HotKey.
|
|
//
|
|
Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2);
|
|
if (!EFI_ERROR (Status)) {
|
|
//
|
|
// Register the default HotKey F9 and F10 again.
|
|
//
|
|
HotKey.UnicodeChar = CHAR_NULL;
|
|
HotKey.ScanCode = SCAN_F10;
|
|
NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL);
|
|
ASSERT (NewString != NULL);
|
|
FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString);
|
|
|
|
HotKey.ScanCode = SCAN_F9;
|
|
NewString = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL);
|
|
ASSERT (NewString != NULL);
|
|
FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
/**
|
|
This is the default unload handle for display core drivers.
|
|
|
|
@param[in] ImageHandle The drivers' driver image.
|
|
|
|
@retval EFI_SUCCESS The image is unloaded.
|
|
@retval Others Failed to unload the image.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
EFIAPI
|
|
UnloadDisplayEngine (
|
|
IN EFI_HANDLE ImageHandle
|
|
)
|
|
{
|
|
HiiRemovePackages(gHiiHandle);
|
|
|
|
FreeDisplayStrings ();
|
|
|
|
if (gHighligthMenuInfo.OpCode != NULL) {
|
|
FreePool (gHighligthMenuInfo.OpCode);
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|