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