/** @file | |
Entry and initialization module for the browser. | |
Copyright (c) 2007 - 2015, 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} | |
}; | |
BOOLEAN gMisMatch; | |
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 *gConfirmDefaultMsg; | |
CHAR16 *gConfirmSubmitMsg; | |
CHAR16 *gConfirmDiscardMsg; | |
CHAR16 *gConfirmResetMsg; | |
CHAR16 *gConfirmExitMsg; | |
CHAR16 *gConfirmSubmitMsg2nd; | |
CHAR16 *gConfirmDefaultMsg2nd; | |
CHAR16 *gConfirmResetMsg2nd; | |
CHAR16 *gConfirmExitMsg2nd; | |
CHAR16 *gConfirmOpt; | |
CHAR16 *gConfirmOptYes; | |
CHAR16 *gConfirmOptNo; | |
CHAR16 *gConfirmMsgConnect; | |
CHAR16 *gConfirmMsgEnd; | |
CHAR16 *gPasswordUnsupported; | |
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); | |
gConfirmDefaultMsg = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE), gHiiHandle); | |
gConfirmDiscardMsg = GetToken (STRING_TOKEN (CONFIRM_DISCARD_MESSAGE), gHiiHandle); | |
gConfirmSubmitMsg = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE), gHiiHandle); | |
gConfirmResetMsg = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE), gHiiHandle); | |
gConfirmExitMsg = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE), gHiiHandle); | |
gConfirmDefaultMsg2nd = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE_2ND), gHiiHandle); | |
gConfirmSubmitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE_2ND), gHiiHandle); | |
gConfirmResetMsg2nd = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE_2ND), gHiiHandle); | |
gConfirmExitMsg2nd = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE_2ND), gHiiHandle); | |
gConfirmOpt = GetToken (STRING_TOKEN (CONFIRM_OPTION), gHiiHandle); | |
gConfirmOptYes = GetToken (STRING_TOKEN (CONFIRM_OPTION_YES), gHiiHandle); | |
gConfirmOptNo = GetToken (STRING_TOKEN (CONFIRM_OPTION_NO), gHiiHandle); | |
gConfirmMsgConnect = GetToken (STRING_TOKEN (CONFIRM_OPTION_CONNECT), gHiiHandle); | |
gConfirmMsgEnd = GetToken (STRING_TOKEN (CONFIRM_OPTION_END), gHiiHandle); | |
gPasswordUnsupported = GetToken (STRING_TOKEN (PASSWORD_NOT_SUPPORTED ), 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); | |
FreePool (gConfirmDefaultMsg); | |
FreePool (gConfirmSubmitMsg); | |
FreePool (gConfirmDiscardMsg); | |
FreePool (gConfirmResetMsg); | |
FreePool (gConfirmExitMsg); | |
FreePool (gConfirmDefaultMsg2nd); | |
FreePool (gConfirmSubmitMsg2nd); | |
FreePool (gConfirmResetMsg2nd); | |
FreePool (gConfirmExitMsg2nd); | |
FreePool (gConfirmOpt); | |
FreePool (gConfirmOptYes); | |
FreePool (gConfirmOptNo); | |
FreePool (gConfirmMsgConnect); | |
FreePool (gConfirmMsgEnd); | |
FreePool (gPasswordUnsupported); | |
} | |
/** | |
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; | |
} | |
/** | |
Get the index info for this opcode. | |
@param OpCode The input opcode for the statement. | |
@retval The index of this statement. | |
**/ | |
UINTN | |
GetIndexInfoForOpcode ( | |
IN EFI_IFR_OP_HEADER *OpCode | |
) | |
{ | |
LIST_ENTRY *NewPos; | |
UI_MENU_OPTION *MenuOption; | |
UINTN Index; | |
NewPos = gMenuOption.ForwardLink; | |
Index = 0; | |
for (NewPos = gMenuOption.ForwardLink; NewPos != &gMenuOption; NewPos = NewPos->ForwardLink){ | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
if (CompareMem (MenuOption->ThisTag->OpCode, OpCode, OpCode->Length) == 0) { | |
if (MenuOption->ThisTag->OpCode == OpCode) { | |
return Index; | |
} | |
Index ++; | |
} | |
} | |
return Index; | |
} | |
/** | |
Is this the saved highlight statement. | |
@param HighLightedStatement The input highlight statement. | |
@retval TRUE This is the highlight statement. | |
@retval FALSE This is not the highlight statement. | |
**/ | |
BOOLEAN | |
IsSavedHighlightStatement ( | |
IN FORM_DISPLAY_ENGINE_STATEMENT *HighLightedStatement | |
) | |
{ | |
if ((gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle) && | |
(gFormData->FormId == gHighligthMenuInfo.FormId)) { | |
if (gHighligthMenuInfo.HLTQuestionId != 0) { | |
return (BOOLEAN) (gHighligthMenuInfo.HLTQuestionId == GetQuestionIdInfo (HighLightedStatement->OpCode)); | |
} else { | |
if (CompareMem (gHighligthMenuInfo.HLTOpCode, HighLightedStatement->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) { | |
if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(HighLightedStatement->OpCode)) { | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Is this the highlight menu. | |
@param MenuOption The input Menu option. | |
@retval TRUE This is the highlight menu option. | |
@retval FALSE This is not the highlight menu option. | |
**/ | |
BOOLEAN | |
IsHighLightMenuOption ( | |
IN UI_MENU_OPTION *MenuOption | |
) | |
{ | |
if (gHighligthMenuInfo.HLTQuestionId != 0) { | |
if (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.HLTQuestionId) { | |
return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence); | |
} | |
} else { | |
if(CompareMem (gHighligthMenuInfo.HLTOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) { | |
if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) { | |
return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence); | |
} else { | |
return FALSE; | |
} | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Find the highlight menu. | |
If the input is NULL, base on the record highlight info in | |
gHighligthMenuInfo to find the last highlight menu. | |
@param HighLightedStatement The input highlight statement. | |
@retval The highlight menu index. | |
**/ | |
LIST_ENTRY * | |
FindHighLightMenuOption ( | |
IN FORM_DISPLAY_ENGINE_STATEMENT *HighLightedStatement | |
) | |
{ | |
LIST_ENTRY *NewPos; | |
UI_MENU_OPTION *MenuOption; | |
NewPos = gMenuOption.ForwardLink; | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
if (HighLightedStatement != NULL) { | |
while (MenuOption->ThisTag != HighLightedStatement) { | |
NewPos = NewPos->ForwardLink; | |
if (NewPos == &gMenuOption) { | |
// | |
// Not Found it, break | |
// | |
break; | |
} | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
} | |
// | |
// Must find the highlight statement. | |
// | |
ASSERT (NewPos != &gMenuOption); | |
} else { | |
while (!IsHighLightMenuOption (MenuOption)) { | |
NewPos = NewPos->ForwardLink; | |
if (NewPos == &gMenuOption) { | |
// | |
// Not Found it, break | |
// | |
break; | |
} | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
} | |
// | |
// Highlight statement has disappear (suppressed/disableed) | |
// | |
if (NewPos == &gMenuOption) { | |
NewPos = NULL; | |
} | |
} | |
return NewPos; | |
} | |
/** | |
Is this the Top of screen menu. | |
@param MenuOption The input Menu option. | |
@retval TRUE This is the Top of screen menu option. | |
@retval FALSE This is not the Top of screen menu option. | |
**/ | |
BOOLEAN | |
IsTopOfScreeMenuOption ( | |
IN UI_MENU_OPTION *MenuOption | |
) | |
{ | |
if (gHighligthMenuInfo.TOSQuestionId != 0) { | |
return (BOOLEAN) (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.TOSQuestionId); | |
} | |
if(CompareMem (gHighligthMenuInfo.TOSOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.TOSOpCode->Length) == 0) { | |
if (gHighligthMenuInfo.TOSIndex == 0 || gHighligthMenuInfo.TOSIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) { | |
return TRUE; | |
} else { | |
return FALSE; | |
} | |
} | |
return FALSE; | |
} | |
/** | |
Find the Top of screen menu. | |
If the input is NULL, base on the record highlight info in | |
gHighligthMenuInfo to find the last highlight menu. | |
@param HighLightedStatement The input highlight statement. | |
@retval The highlight menu index. | |
**/ | |
LIST_ENTRY * | |
FindTopOfScreenMenuOption ( | |
VOID | |
) | |
{ | |
LIST_ENTRY *NewPos; | |
UI_MENU_OPTION *MenuOption; | |
NewPos = gMenuOption.ForwardLink; | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
while (!IsTopOfScreeMenuOption(MenuOption)) { | |
NewPos = NewPos->ForwardLink; | |
if (NewPos == &gMenuOption) { | |
// | |
// Not Found it, break | |
// | |
break; | |
} | |
MenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
} | |
// | |
// Last time top of screen menu has disappeared. | |
// | |
if (NewPos == &gMenuOption) { | |
NewPos = NULL; | |
} | |
return NewPos; | |
} | |
/** | |
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 | |
) | |
{ | |
UINTN TopRow; | |
UINTN BottomRow; | |
UI_MENU_OPTION *MenuOption; | |
UINTN TmpValue; | |
TopRow = gStatementDimensions.TopRow + SCROLL_ARROW_HEIGHT; | |
BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT; | |
if (gMisMatch) { | |
// | |
// Reenter caused by option mismatch or auto exit caused by refresh form(refresh interval/guid), | |
// base on the record highlight info to find the highlight menu. | |
// | |
ASSERT (gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle && | |
gFormData->FormId == gHighligthMenuInfo.FormId); | |
*HighlightMenu = FindHighLightMenuOption(NULL); | |
if (*HighlightMenu != NULL) { | |
// | |
// Update skip info for this highlight menu. | |
// | |
MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); | |
UpdateOptionSkipLines (MenuOption); | |
// | |
// Found the last time highlight menu. | |
// | |
*TopOfScreen = FindTopOfScreenMenuOption(); | |
if (*TopOfScreen != NULL) { | |
// | |
// Found the last time selectable top of screen menu. | |
// | |
AdjustDateAndTimePosition(TRUE, TopOfScreen); | |
MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen); | |
UpdateOptionSkipLines (MenuOption); | |
*SkipValue = gHighligthMenuInfo.SkipValue; | |
} else { | |
// | |
// Not found last time top of screen menu, so base on current highlight menu | |
// to find the new top of screen menu. | |
// Make the current highlight menu at the bottom of the form to calculate the | |
// top of screen menu. | |
// | |
if (MenuOption->Skip >= BottomRow - TopRow) { | |
*TopOfScreen = *HighlightMenu; | |
TmpValue = 0; | |
} else { | |
*TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); | |
} | |
*SkipValue = TmpValue; | |
} | |
} else { | |
// | |
// Last time highlight menu has disappear, find the first highlightable menu as the defalut one. | |
// | |
*HighlightMenu = gMenuOption.ForwardLink; | |
if (!IsListEmpty (&gMenuOption)) { | |
MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE); | |
} | |
*TopOfScreen = gMenuOption.ForwardLink; | |
*SkipValue = 0; | |
} | |
gMisMatch = FALSE; | |
} else if (FormData->HighLightedStatement != NULL) { | |
if (IsSavedHighlightStatement (FormData->HighLightedStatement)) { | |
// | |
// Input highlight menu is same as last time highlight menu. | |
// Base on last time highlight menu to set the top of screen menu and highlight menu. | |
// | |
*HighlightMenu = FindHighLightMenuOption(NULL); | |
ASSERT (*HighlightMenu != NULL); | |
// | |
// Update skip info for this highlight menu. | |
// | |
MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); | |
UpdateOptionSkipLines (MenuOption); | |
*TopOfScreen = FindTopOfScreenMenuOption(); | |
if (*TopOfScreen == NULL) { | |
// | |
// Not found last time top of screen menu, so base on current highlight menu | |
// to find the new top of screen menu. | |
// Make the current highlight menu at the bottom of the form to calculate the | |
// top of screen menu. | |
// | |
if (MenuOption->Skip >= BottomRow - TopRow) { | |
*TopOfScreen = *HighlightMenu; | |
TmpValue = 0; | |
} else { | |
*TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); | |
} | |
*SkipValue = TmpValue; | |
} else { | |
AdjustDateAndTimePosition(TRUE, TopOfScreen); | |
MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen); | |
UpdateOptionSkipLines (MenuOption); | |
*SkipValue = gHighligthMenuInfo.SkipValue; | |
} | |
AdjustDateAndTimePosition(TRUE, TopOfScreen); | |
} else { | |
// | |
// Input highlight menu is not save as last time highlight menu. | |
// | |
*HighlightMenu = FindHighLightMenuOption(FormData->HighLightedStatement); | |
MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu); | |
UpdateOptionSkipLines (MenuOption); | |
// | |
// Make the current highlight menu at the bottom of the form to calculate the | |
// top of screen menu. | |
// | |
if (MenuOption->Skip >= BottomRow - TopRow) { | |
*TopOfScreen = *HighlightMenu; | |
TmpValue = 0; | |
} else { | |
*TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue); | |
} | |
*SkipValue = TmpValue; | |
} | |
AdjustDateAndTimePosition(TRUE, TopOfScreen); | |
} else { | |
// | |
// If not has input highlight statement, just return the first one in this form. | |
// | |
*TopOfScreen = gMenuOption.ForwardLink; | |
*HighlightMenu = gMenuOption.ForwardLink; | |
if (!IsListEmpty (&gMenuOption)) { | |
MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE); | |
} | |
*SkipValue = 0; | |
} | |
} | |
/** | |
Record the highlight menu and top of screen menu info. | |
@param Highlight The menu opton which is highlight. | |
@param TopOfScreen The menu opton which is at the top of the form. | |
@param SkipValue The skip line info for the top of screen menu. | |
**/ | |
VOID | |
UpdateHighlightMenuInfo ( | |
IN LIST_ENTRY *Highlight, | |
IN LIST_ENTRY *TopOfScreen, | |
IN UINTN SkipValue | |
) | |
{ | |
UI_MENU_OPTION *MenuOption; | |
FORM_DISPLAY_ENGINE_STATEMENT *Statement; | |
gHighligthMenuInfo.HiiHandle = gFormData->HiiHandle; | |
gHighligthMenuInfo.FormId = gFormData->FormId; | |
gHighligthMenuInfo.SkipValue = (UINT16)SkipValue; | |
if (!IsListEmpty (&gMenuOption)) { | |
MenuOption = MENU_OPTION_FROM_LINK (Highlight); | |
Statement = MenuOption->ThisTag; | |
gUserInput->SelectedStatement = Statement; | |
gHighligthMenuInfo.HLTSequence = MenuOption->Sequence; | |
gHighligthMenuInfo.HLTQuestionId = GetQuestionIdInfo(Statement->OpCode); | |
if (gHighligthMenuInfo.HLTQuestionId == 0) { | |
// | |
// if question id == 0, save the opcode buffer.. | |
// | |
if (gHighligthMenuInfo.HLTOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.HLTOpCode); | |
} | |
gHighligthMenuInfo.HLTOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode); | |
ASSERT (gHighligthMenuInfo.HLTOpCode != NULL); | |
gHighligthMenuInfo.HLTIndex = GetIndexInfoForOpcode(Statement->OpCode); | |
} | |
MenuOption = MENU_OPTION_FROM_LINK (TopOfScreen); | |
Statement = MenuOption->ThisTag; | |
gHighligthMenuInfo.TOSQuestionId = GetQuestionIdInfo(Statement->OpCode); | |
if (gHighligthMenuInfo.TOSQuestionId == 0) { | |
// | |
// if question id == 0, save the opcode buffer.. | |
// | |
if (gHighligthMenuInfo.TOSOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.TOSOpCode); | |
} | |
gHighligthMenuInfo.TOSOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode); | |
ASSERT (gHighligthMenuInfo.TOSOpCode != NULL); | |
gHighligthMenuInfo.TOSIndex = GetIndexInfoForOpcode(Statement->OpCode); | |
} | |
} else { | |
gUserInput->SelectedStatement = NULL; | |
gHighligthMenuInfo.HLTSequence = 0; | |
gHighligthMenuInfo.HLTQuestionId = 0; | |
if (gHighligthMenuInfo.HLTOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.HLTOpCode); | |
} | |
gHighligthMenuInfo.HLTOpCode = NULL; | |
gHighligthMenuInfo.HLTIndex = 0; | |
gHighligthMenuInfo.TOSQuestionId = 0; | |
if (gHighligthMenuInfo.TOSOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.TOSOpCode); | |
} | |
gHighligthMenuInfo.TOSOpCode = NULL; | |
gHighligthMenuInfo.TOSIndex = 0; | |
} | |
} | |
/** | |
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; | |
} | |
/** | |
Double confirm with user about the action. | |
@param Action The user input action. | |
@retval TRUE User confirm with the input or not need user confirm. | |
@retval FALSE User want ignore this input. | |
**/ | |
BOOLEAN | |
FxConfirmPopup ( | |
IN UINT32 Action | |
) | |
{ | |
EFI_INPUT_KEY Key; | |
CHAR16 *CfmStr; | |
UINTN CfmStrLen; | |
UINT32 CheckFlags; | |
BOOLEAN RetVal; | |
UINTN CatLen; | |
CfmStrLen = 0; | |
CatLen = StrLen (gConfirmMsgConnect); | |
// | |
// Below action need extra popup dialog to confirm. | |
// | |
CheckFlags = BROWSER_ACTION_DISCARD | | |
BROWSER_ACTION_DEFAULT | | |
BROWSER_ACTION_SUBMIT | | |
BROWSER_ACTION_RESET | | |
BROWSER_ACTION_EXIT; | |
// | |
// Not need to confirm with user, just return TRUE. | |
// | |
if ((Action & CheckFlags) == 0) { | |
return TRUE; | |
} | |
if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) { | |
CfmStrLen += StrLen (gConfirmDiscardMsg); | |
} | |
if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { | |
if (CfmStrLen != 0) { | |
CfmStrLen += CatLen; | |
} | |
CfmStrLen += StrLen (gConfirmDefaultMsg); | |
} | |
if ((Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) { | |
if (CfmStrLen != 0) { | |
CfmStrLen += CatLen; | |
} | |
CfmStrLen += StrLen (gConfirmSubmitMsg); | |
} | |
if ((Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) { | |
if (CfmStrLen != 0) { | |
CfmStrLen += CatLen; | |
} | |
CfmStrLen += StrLen (gConfirmResetMsg); | |
} | |
if ((Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) { | |
if (CfmStrLen != 0) { | |
CfmStrLen += CatLen; | |
} | |
CfmStrLen += StrLen (gConfirmExitMsg); | |
} | |
// | |
// Allocate buffer to save the string. | |
// String + "?" + "\0" | |
// | |
CfmStr = AllocateZeroPool ((CfmStrLen + 1 + 1) * sizeof (CHAR16)); | |
ASSERT (CfmStr != NULL); | |
if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) { | |
StrCpy (CfmStr, gConfirmDiscardMsg); | |
} | |
if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { | |
if (CfmStr[0] != 0) { | |
StrCat (CfmStr, gConfirmMsgConnect); | |
StrCat (CfmStr, gConfirmDefaultMsg2nd); | |
} else { | |
StrCpy (CfmStr, gConfirmDefaultMsg); | |
} | |
} | |
if ((Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) { | |
if (CfmStr[0] != 0) { | |
StrCat (CfmStr, gConfirmMsgConnect); | |
StrCat (CfmStr, gConfirmSubmitMsg2nd); | |
} else { | |
StrCpy (CfmStr, gConfirmSubmitMsg); | |
} | |
} | |
if ((Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) { | |
if (CfmStr[0] != 0) { | |
StrCat (CfmStr, gConfirmMsgConnect); | |
StrCat (CfmStr, gConfirmResetMsg2nd); | |
} else { | |
StrCpy (CfmStr, gConfirmResetMsg); | |
} | |
} | |
if ((Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) { | |
if (CfmStr[0] != 0) { | |
StrCat (CfmStr, gConfirmMsgConnect); | |
StrCat (CfmStr, gConfirmExitMsg2nd); | |
} else { | |
StrCpy (CfmStr, gConfirmExitMsg); | |
} | |
} | |
StrCat (CfmStr, gConfirmMsgEnd); | |
do { | |
CreateDialog (&Key, gEmptyString, CfmStr, gConfirmOpt, gEmptyString, NULL); | |
} while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) && | |
((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptNo[0] | UPPER_LOWER_CASE_OFFSET))); | |
if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) { | |
RetVal = TRUE; | |
} else { | |
RetVal = FALSE; | |
} | |
FreePool (CfmStr); | |
return RetVal; | |
} | |
/** | |
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); | |
if (!IsListEmpty (&gMenuOption)) { | |
NextMenuOption = MENU_OPTION_FROM_LINK (NewPos); | |
gUserInput->SelectedStatement = NextMenuOption->ThisTag; | |
} | |
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)) { | |
if (gMisMatch) { | |
return EFI_SUCCESS; | |
} else { | |
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; | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
if (SkipHighLight) { | |
SkipHighLight = FALSE; | |
MenuOption = SavedMenuOption; | |
RefreshKeyHelp(gFormData, SavedMenuOption->ThisTag, FALSE); | |
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); | |
RefreshKeyHelp(gFormData, MenuOption->ThisTag, FALSE); | |
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) { | |
gMisMatch = TRUE; | |
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; | |
} | |
gUserInput->Action = BROWSER_ACTION_FORM_EXIT; | |
ControlFlag = CfExit; | |
break; | |
case CfUiHotKey: | |
ControlFlag = CfRepaint; | |
ASSERT (HotKey != NULL); | |
if (FxConfirmPopup(HotKey->Action)) { | |
gUserInput->Action = HotKey->Action; | |
if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) { | |
gUserInput->DefaultId = HotKey->DefaultId; | |
} | |
ControlFlag = CfExit; | |
} else { | |
Repaint = TRUE; | |
NewLine = TRUE; | |
ControlFlag = CfRepaint; | |
} | |
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); | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
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); | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
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); | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
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); | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
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); | |
UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue); | |
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.HLTOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.HLTOpCode); | |
} | |
if (gHighligthMenuInfo.TOSOpCode != NULL) { | |
FreePool (gHighligthMenuInfo.TOSOpCode); | |
} | |
return EFI_SUCCESS; | |
} |