Home | History | Annotate | Download | only in DisplayEngineDxe
      1 /** @file
      2 Entry and initialization module for the browser.
      3 
      4 Copyright (c) 2007 - 2016, Intel Corporation. All rights reserved.<BR>
      5 Copyright (c) 2014, Hewlett-Packard Development Company, L.P.<BR>
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include "FormDisplay.h"
     17 
     18 //
     19 // Search table for UiDisplayMenu()
     20 //
     21 SCAN_CODE_TO_SCREEN_OPERATION     gScanCodeToOperation[] = {
     22   {
     23     SCAN_UP,
     24     UiUp,
     25   },
     26   {
     27     SCAN_DOWN,
     28     UiDown,
     29   },
     30   {
     31     SCAN_PAGE_UP,
     32     UiPageUp,
     33   },
     34   {
     35     SCAN_PAGE_DOWN,
     36     UiPageDown,
     37   },
     38   {
     39     SCAN_ESC,
     40     UiReset,
     41   },
     42   {
     43     SCAN_LEFT,
     44     UiLeft,
     45   },
     46   {
     47     SCAN_RIGHT,
     48     UiRight,
     49   }
     50 };
     51 
     52 UINTN mScanCodeNumber = ARRAY_SIZE (gScanCodeToOperation);
     53 
     54 SCREEN_OPERATION_T0_CONTROL_FLAG  gScreenOperationToControlFlag[] = {
     55   {
     56     UiNoOperation,
     57     CfUiNoOperation,
     58   },
     59   {
     60     UiSelect,
     61     CfUiSelect,
     62   },
     63   {
     64     UiUp,
     65     CfUiUp,
     66   },
     67   {
     68     UiDown,
     69     CfUiDown,
     70   },
     71   {
     72     UiLeft,
     73     CfUiLeft,
     74   },
     75   {
     76     UiRight,
     77     CfUiRight,
     78   },
     79   {
     80     UiReset,
     81     CfUiReset,
     82   },
     83   {
     84     UiPageUp,
     85     CfUiPageUp,
     86   },
     87   {
     88     UiPageDown,
     89     CfUiPageDown
     90   },
     91   {
     92     UiHotKey,
     93     CfUiHotKey
     94   }
     95 };
     96 
     97 EFI_GUID  gDisplayEngineGuid = {
     98   0xE38C1029, 0xE38F, 0x45b9, {0x8F, 0x0D, 0xE2, 0xE6, 0x0B, 0xC9, 0xB2, 0x62}
     99 };
    100 
    101 BOOLEAN                       gMisMatch;
    102 EFI_SCREEN_DESCRIPTOR         gStatementDimensions;
    103 BOOLEAN                       mStatementLayoutIsChanged = TRUE;
    104 USER_INPUT                    *gUserInput;
    105 FORM_DISPLAY_ENGINE_FORM      *gFormData;
    106 EFI_HII_HANDLE                gHiiHandle;
    107 UINT16                        gDirection;
    108 LIST_ENTRY                    gMenuOption;
    109 DISPLAY_HIGHLIGHT_MENU_INFO   gHighligthMenuInfo = {0};
    110 BOOLEAN                       mIsFirstForm = TRUE;
    111 FORM_ENTRY_INFO               gOldFormEntry = {0};
    112 
    113 //
    114 // Browser Global Strings
    115 //
    116 CHAR16            *gReconnectConfirmChanges;
    117 CHAR16            *gReconnectFail;
    118 CHAR16            *gReconnectRequired;
    119 CHAR16            *gChangesOpt;
    120 CHAR16            *gFormNotFound;
    121 CHAR16            *gNoSubmitIf;
    122 CHAR16            *gBrowserError;
    123 CHAR16            *gSaveFailed;
    124 CHAR16            *gNoSubmitIfFailed;
    125 CHAR16            *gSaveProcess;
    126 CHAR16            *gSaveNoSubmitProcess;
    127 CHAR16            *gDiscardChange;
    128 CHAR16            *gJumpToFormSet;
    129 CHAR16            *gCheckError;
    130 CHAR16            *gPromptForData;
    131 CHAR16            *gPromptForPassword;
    132 CHAR16            *gPromptForNewPassword;
    133 CHAR16            *gConfirmPassword;
    134 CHAR16            *gConfirmError;
    135 CHAR16            *gPassowordInvalid;
    136 CHAR16            *gPressEnter;
    137 CHAR16            *gEmptyString;
    138 CHAR16            *gMiniString;
    139 CHAR16            *gOptionMismatch;
    140 CHAR16            *gFormSuppress;
    141 CHAR16            *gProtocolNotFound;
    142 CHAR16            *gConfirmDefaultMsg;
    143 CHAR16            *gConfirmSubmitMsg;
    144 CHAR16            *gConfirmDiscardMsg;
    145 CHAR16            *gConfirmResetMsg;
    146 CHAR16            *gConfirmExitMsg;
    147 CHAR16            *gConfirmSubmitMsg2nd;
    148 CHAR16            *gConfirmDefaultMsg2nd;
    149 CHAR16            *gConfirmResetMsg2nd;
    150 CHAR16            *gConfirmExitMsg2nd;
    151 CHAR16            *gConfirmOpt;
    152 CHAR16            *gConfirmOptYes;
    153 CHAR16            *gConfirmOptNo;
    154 CHAR16            *gConfirmMsgConnect;
    155 CHAR16            *gConfirmMsgEnd;
    156 CHAR16            *gPasswordUnsupported;
    157 CHAR16            gModalSkipColumn;
    158 CHAR16            gPromptBlockWidth;
    159 CHAR16            gOptionBlockWidth;
    160 CHAR16            gHelpBlockWidth;
    161 CHAR16            *mUnknownString;
    162 
    163 FORM_DISPLAY_DRIVER_PRIVATE_DATA  mPrivateData = {
    164   FORM_DISPLAY_DRIVER_SIGNATURE,
    165   NULL,
    166   {
    167     FormDisplay,
    168     DriverClearDisplayPage,
    169     ConfirmDataChange
    170   }
    171 };
    172 
    173 
    174 /**
    175   Get the string based on the StringId and HII Package List Handle.
    176 
    177   @param  Token                  The String's ID.
    178   @param  HiiHandle              The package list in the HII database to search for
    179                                  the specified string.
    180 
    181   @return The output string.
    182 
    183 **/
    184 CHAR16 *
    185 GetToken (
    186   IN  EFI_STRING_ID                Token,
    187   IN  EFI_HII_HANDLE               HiiHandle
    188   )
    189 {
    190   EFI_STRING  String;
    191 
    192   String = HiiGetString (HiiHandle, Token, NULL);
    193   if (String == NULL) {
    194     String = AllocateCopyPool (StrSize (mUnknownString), mUnknownString);
    195     ASSERT (String != NULL);
    196   }
    197 
    198   return (CHAR16 *) String;
    199 }
    200 
    201 
    202 /**
    203   Initialize the HII String Token to the correct values.
    204 
    205 **/
    206 VOID
    207 InitializeDisplayStrings (
    208   VOID
    209   )
    210 {
    211   gReconnectConfirmChanges = GetToken (STRING_TOKEN (RECONNECT_CONFIRM_CHANGES), gHiiHandle);
    212   mUnknownString        = GetToken (STRING_TOKEN (UNKNOWN_STRING), gHiiHandle);
    213   gSaveFailed           = GetToken (STRING_TOKEN (SAVE_FAILED), gHiiHandle);
    214   gNoSubmitIfFailed     = GetToken (STRING_TOKEN (NO_SUBMIT_IF_CHECK_FAILED), gHiiHandle);
    215   gReconnectFail        = GetToken (STRING_TOKEN (RECONNECT_FAILED), gHiiHandle);
    216   gReconnectRequired    = GetToken (STRING_TOKEN (RECONNECT_REQUIRED), gHiiHandle);
    217   gChangesOpt           = GetToken (STRING_TOKEN (RECONNECT_CHANGES_OPTIONS), gHiiHandle);
    218   gSaveProcess          = GetToken (STRING_TOKEN (DISCARD_OR_JUMP), gHiiHandle);
    219   gSaveNoSubmitProcess  = GetToken (STRING_TOKEN (DISCARD_OR_CHECK), gHiiHandle);
    220   gDiscardChange        = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_DISCARD), gHiiHandle);
    221   gJumpToFormSet        = GetToken (STRING_TOKEN (DISCARD_OR_JUMP_JUMP), gHiiHandle);
    222   gCheckError           = GetToken (STRING_TOKEN (DISCARD_OR_CHECK_CHECK), gHiiHandle);
    223   gPromptForData        = GetToken (STRING_TOKEN (PROMPT_FOR_DATA), gHiiHandle);
    224   gPromptForPassword    = GetToken (STRING_TOKEN (PROMPT_FOR_PASSWORD), gHiiHandle);
    225   gPromptForNewPassword = GetToken (STRING_TOKEN (PROMPT_FOR_NEW_PASSWORD), gHiiHandle);
    226   gConfirmPassword      = GetToken (STRING_TOKEN (CONFIRM_PASSWORD), gHiiHandle);
    227   gConfirmError         = GetToken (STRING_TOKEN (CONFIRM_ERROR), gHiiHandle);
    228   gPassowordInvalid     = GetToken (STRING_TOKEN (PASSWORD_INVALID), gHiiHandle);
    229   gPressEnter           = GetToken (STRING_TOKEN (PRESS_ENTER), gHiiHandle);
    230   gEmptyString          = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
    231   gMiniString           = GetToken (STRING_TOKEN (MINI_STRING), gHiiHandle);
    232   gOptionMismatch       = GetToken (STRING_TOKEN (OPTION_MISMATCH), gHiiHandle);
    233   gFormSuppress         = GetToken (STRING_TOKEN (FORM_SUPPRESSED), gHiiHandle);
    234   gProtocolNotFound     = GetToken (STRING_TOKEN (PROTOCOL_NOT_FOUND), gHiiHandle);
    235   gFormNotFound         = GetToken (STRING_TOKEN (STATUS_BROWSER_FORM_NOT_FOUND), gHiiHandle);
    236   gNoSubmitIf           = GetToken (STRING_TOKEN (STATUS_BROWSER_NO_SUBMIT_IF), gHiiHandle);
    237   gBrowserError         = GetToken (STRING_TOKEN (STATUS_BROWSER_ERROR), gHiiHandle);
    238   gConfirmDefaultMsg    = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE), gHiiHandle);
    239   gConfirmDiscardMsg    = GetToken (STRING_TOKEN (CONFIRM_DISCARD_MESSAGE), gHiiHandle);
    240   gConfirmSubmitMsg     = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE), gHiiHandle);
    241   gConfirmResetMsg      = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE), gHiiHandle);
    242   gConfirmExitMsg       = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE), gHiiHandle);
    243   gConfirmDefaultMsg2nd = GetToken (STRING_TOKEN (CONFIRM_DEFAULT_MESSAGE_2ND), gHiiHandle);
    244   gConfirmSubmitMsg2nd  = GetToken (STRING_TOKEN (CONFIRM_SUBMIT_MESSAGE_2ND), gHiiHandle);
    245   gConfirmResetMsg2nd   = GetToken (STRING_TOKEN (CONFIRM_RESET_MESSAGE_2ND), gHiiHandle);
    246   gConfirmExitMsg2nd    = GetToken (STRING_TOKEN (CONFIRM_EXIT_MESSAGE_2ND), gHiiHandle);
    247   gConfirmOpt           = GetToken (STRING_TOKEN (CONFIRM_OPTION), gHiiHandle);
    248   gConfirmOptYes        = GetToken (STRING_TOKEN (CONFIRM_OPTION_YES), gHiiHandle);
    249   gConfirmOptNo         = GetToken (STRING_TOKEN (CONFIRM_OPTION_NO), gHiiHandle);
    250   gConfirmMsgConnect    = GetToken (STRING_TOKEN (CONFIRM_OPTION_CONNECT), gHiiHandle);
    251   gConfirmMsgEnd        = GetToken (STRING_TOKEN (CONFIRM_OPTION_END), gHiiHandle);
    252   gPasswordUnsupported  = GetToken (STRING_TOKEN (PASSWORD_NOT_SUPPORTED ), gHiiHandle);
    253 }
    254 
    255 /**
    256   Free up the resource allocated for all strings required
    257   by Setup Browser.
    258 
    259 **/
    260 VOID
    261 FreeDisplayStrings (
    262   VOID
    263   )
    264 {
    265   FreePool (mUnknownString);
    266   FreePool (gEmptyString);
    267   FreePool (gSaveFailed);
    268   FreePool (gNoSubmitIfFailed);
    269   FreePool (gReconnectFail);
    270   FreePool (gReconnectRequired);
    271   FreePool (gChangesOpt);
    272   FreePool (gReconnectConfirmChanges);
    273   FreePool (gSaveProcess);
    274   FreePool (gSaveNoSubmitProcess);
    275   FreePool (gDiscardChange);
    276   FreePool (gJumpToFormSet);
    277   FreePool (gCheckError);
    278   FreePool (gPromptForData);
    279   FreePool (gPromptForPassword);
    280   FreePool (gPromptForNewPassword);
    281   FreePool (gConfirmPassword);
    282   FreePool (gConfirmError);
    283   FreePool (gPassowordInvalid);
    284   FreePool (gPressEnter);
    285   FreePool (gMiniString);
    286   FreePool (gOptionMismatch);
    287   FreePool (gFormSuppress);
    288   FreePool (gProtocolNotFound);
    289   FreePool (gBrowserError);
    290   FreePool (gNoSubmitIf);
    291   FreePool (gFormNotFound);
    292   FreePool (gConfirmDefaultMsg);
    293   FreePool (gConfirmSubmitMsg);
    294   FreePool (gConfirmDiscardMsg);
    295   FreePool (gConfirmResetMsg);
    296   FreePool (gConfirmExitMsg);
    297   FreePool (gConfirmDefaultMsg2nd);
    298   FreePool (gConfirmSubmitMsg2nd);
    299   FreePool (gConfirmResetMsg2nd);
    300   FreePool (gConfirmExitMsg2nd);
    301   FreePool (gConfirmOpt);
    302   FreePool (gConfirmOptYes);
    303   FreePool (gConfirmOptNo);
    304   FreePool (gConfirmMsgConnect);
    305   FreePool (gConfirmMsgEnd);
    306   FreePool (gPasswordUnsupported);
    307 }
    308 
    309 /**
    310   Get prompt string id from the opcode data buffer.
    311 
    312   @param  OpCode                 The input opcode buffer.
    313 
    314   @return The prompt string id.
    315 
    316 **/
    317 EFI_STRING_ID
    318 GetPrompt (
    319   IN EFI_IFR_OP_HEADER     *OpCode
    320   )
    321 {
    322   EFI_IFR_STATEMENT_HEADER  *Header;
    323 
    324   if (OpCode->Length <= sizeof (EFI_IFR_OP_HEADER)) {
    325     return 0;
    326   }
    327 
    328   Header = (EFI_IFR_STATEMENT_HEADER  *) (OpCode + 1);
    329 
    330   return Header->Prompt;
    331 }
    332 
    333 /**
    334   Get the supported width for a particular op-code
    335 
    336   @param  MenuOption             The menu option.
    337   @param  AdjustWidth            The width which is saved for the space.
    338 
    339   @return Returns the number of CHAR16 characters that is support.
    340 
    341 **/
    342 UINT16
    343 GetWidth (
    344   IN  UI_MENU_OPTION     *MenuOption,
    345   OUT UINT16             *AdjustWidth
    346   )
    347 {
    348   CHAR16                        *String;
    349   UINTN                         Size;
    350   EFI_IFR_TEXT                  *TestOp;
    351   UINT16                        ReturnWidth;
    352   FORM_DISPLAY_ENGINE_STATEMENT *Statement;
    353 
    354   Statement = MenuOption->ThisTag;
    355 
    356   //
    357   // For modal form, clean the entire row.
    358   //
    359   if ((gFormData->Attribute & HII_DISPLAY_MODAL) != 0) {
    360     if (AdjustWidth  != NULL) {
    361       *AdjustWidth = LEFT_SKIPPED_COLUMNS;
    362     }
    363     return (UINT16)(gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gModalSkipColumn + LEFT_SKIPPED_COLUMNS));
    364   }
    365 
    366   Size = 0;
    367 
    368   //
    369   // See if the second text parameter is really NULL
    370   //
    371   if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
    372     TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
    373     if (TestOp->TextTwo != 0) {
    374       String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
    375       Size   = StrLen (String);
    376       FreePool (String);
    377     }
    378   }
    379 
    380   if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
    381       (Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
    382       (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
    383       (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
    384       (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
    385       //
    386       // Allow a wide display if text op-code and no secondary text op-code
    387       //
    388       ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
    389       ) {
    390 
    391     //
    392     // Return the space width.
    393     //
    394     if (AdjustWidth != NULL) {
    395       *AdjustWidth = 2;
    396     }
    397     //
    398     // Keep consistent with current behavior.
    399     //
    400     ReturnWidth = (UINT16) (gPromptBlockWidth + gOptionBlockWidth - 2);
    401   } else {
    402     if (AdjustWidth != NULL) {
    403       *AdjustWidth = 1;
    404     }
    405 
    406     ReturnWidth =  (UINT16) (gPromptBlockWidth - 1);
    407   }
    408 
    409   //
    410   // For nest in statement, should the subtitle indent.
    411   //
    412   if (MenuOption->NestInStatement) {
    413     ReturnWidth -= SUBTITLE_INDENT;
    414   }
    415 
    416   return ReturnWidth;
    417 }
    418 
    419 /**
    420   Will copy LineWidth amount of a string in the OutputString buffer and return the
    421   number of CHAR16 characters that were copied into the OutputString buffer.
    422   The output string format is:
    423     Glyph Info + String info + '\0'.
    424 
    425   In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
    426 
    427   @param  InputString            String description for this option.
    428   @param  LineWidth              Width of the desired string to extract in CHAR16
    429                                  characters
    430   @param  GlyphWidth             The glyph width of the begin of the char in the string.
    431   @param  Index                  Where in InputString to start the copy process
    432   @param  OutputString           Buffer to copy the string into
    433 
    434   @return Returns the number of CHAR16 characters that were copied into the OutputString
    435   buffer, include extra glyph info and '\0' info.
    436 
    437 **/
    438 UINT16
    439 GetLineByWidth (
    440   IN      CHAR16                      *InputString,
    441   IN      UINT16                      LineWidth,
    442   IN OUT  UINT16                      *GlyphWidth,
    443   IN OUT  UINTN                       *Index,
    444   OUT     CHAR16                      **OutputString
    445   )
    446 {
    447   UINT16          StrOffset;
    448   UINT16          GlyphOffset;
    449   UINT16          OriginalGlyphWidth;
    450   BOOLEAN         ReturnFlag;
    451   UINT16          LastSpaceOffset;
    452   UINT16          LastGlyphWidth;
    453 
    454   if (InputString == NULL || Index == NULL || OutputString == NULL) {
    455     return 0;
    456   }
    457 
    458   if (LineWidth == 0 || *GlyphWidth == 0) {
    459     return 0;
    460   }
    461 
    462   //
    463   // Save original glyph width.
    464   //
    465   OriginalGlyphWidth = *GlyphWidth;
    466   LastGlyphWidth     = OriginalGlyphWidth;
    467   ReturnFlag         = FALSE;
    468   LastSpaceOffset    = 0;
    469 
    470   //
    471   // 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.
    472   // To avoid displaying this  empty line in screen,  just skip  the two CHARs here.
    473   //
    474   if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
    475     *Index = *Index + 2;
    476   }
    477 
    478   //
    479   // Fast-forward the string and see if there is a carriage-return in the string
    480   //
    481   for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) {
    482     switch (InputString[*Index + StrOffset]) {
    483       case NARROW_CHAR:
    484         *GlyphWidth = 1;
    485         break;
    486 
    487       case WIDE_CHAR:
    488         *GlyphWidth = 2;
    489         break;
    490 
    491       case CHAR_CARRIAGE_RETURN:
    492       case CHAR_LINEFEED:
    493       case CHAR_NULL:
    494         ReturnFlag = TRUE;
    495         break;
    496 
    497       default:
    498         GlyphOffset = GlyphOffset + *GlyphWidth;
    499 
    500         //
    501         // Record the last space info in this line. Will be used in rewind.
    502         //
    503         if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) {
    504           LastSpaceOffset = StrOffset;
    505           LastGlyphWidth  = *GlyphWidth;
    506         }
    507         break;
    508     }
    509 
    510     if (ReturnFlag) {
    511       break;
    512     }
    513   }
    514 
    515   //
    516   // Rewind the string from the maximum size until we see a space to break the line
    517   //
    518   if (GlyphOffset > LineWidth) {
    519     //
    520     // Rewind the string to last space char in this line.
    521     //
    522     if (LastSpaceOffset != 0) {
    523       StrOffset   = LastSpaceOffset;
    524       *GlyphWidth = LastGlyphWidth;
    525     } else {
    526       //
    527       // Roll back to last char in the line width.
    528       //
    529       StrOffset--;
    530     }
    531   }
    532 
    533   //
    534   // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
    535   //
    536   if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
    537     return 0;
    538   }
    539 
    540   //
    541   // Need extra glyph info and '\0' info, so +2.
    542   //
    543   *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16)));
    544   if (*OutputString == NULL) {
    545     return 0;
    546   }
    547 
    548   //
    549   // Save the glyph info at the begin of the string, will used by Print function.
    550   //
    551   if (OriginalGlyphWidth == 1) {
    552     *(*OutputString) = NARROW_CHAR;
    553   } else  {
    554     *(*OutputString) = WIDE_CHAR;
    555   }
    556 
    557   CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16));
    558 
    559   if (InputString[*Index + StrOffset] == CHAR_SPACE) {
    560     //
    561     // Skip the space info at the begin of next line.
    562     //
    563     *Index = (UINT16) (*Index + StrOffset + 1);
    564   } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
    565     //
    566     // Skip the /n or /n/r info.
    567     //
    568     if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
    569       *Index = (UINT16) (*Index + StrOffset + 2);
    570     } else {
    571       *Index = (UINT16) (*Index + StrOffset + 1);
    572     }
    573   } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
    574     //
    575     // Skip the /r or /r/n info.
    576     //
    577     if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
    578       *Index = (UINT16) (*Index + StrOffset + 2);
    579     } else {
    580       *Index = (UINT16) (*Index + StrOffset + 1);
    581     }
    582   } else {
    583     *Index = (UINT16) (*Index + StrOffset);
    584   }
    585 
    586   //
    587   // Include extra glyph info and '\0' info, so +2.
    588   //
    589   return StrOffset + 2;
    590 }
    591 
    592 /**
    593   Add one menu option by specified description and context.
    594 
    595   @param  Statement              Statement of this Menu Option.
    596   @param  MenuItemCount          The index for this Option in the Menu.
    597   @param  NestIn                 Whether this statement is nest in another statement.
    598 
    599 **/
    600 VOID
    601 UiAddMenuOption (
    602   IN FORM_DISPLAY_ENGINE_STATEMENT   *Statement,
    603   IN UINT16                          *MenuItemCount,
    604   IN BOOLEAN                         NestIn
    605   )
    606 {
    607   UI_MENU_OPTION   *MenuOption;
    608   UINTN            Index;
    609   UINTN            Count;
    610   UINT16           NumberOfLines;
    611   UINT16           GlyphWidth;
    612   UINT16           Width;
    613   UINTN            ArrayEntry;
    614   CHAR16           *OutputString;
    615   EFI_STRING_ID    PromptId;
    616 
    617   NumberOfLines = 1;
    618   ArrayEntry    = 0;
    619   GlyphWidth    = 1;
    620   Count         = 1;
    621   MenuOption    = NULL;
    622 
    623   PromptId = GetPrompt (Statement->OpCode);
    624   ASSERT (PromptId != 0);
    625 
    626   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
    627     Count = 3;
    628   }
    629 
    630   for (Index = 0; Index < Count; Index++) {
    631     MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
    632     ASSERT (MenuOption);
    633 
    634     MenuOption->Signature   = UI_MENU_OPTION_SIGNATURE;
    635     MenuOption->Description = GetToken (PromptId, gFormData->HiiHandle);
    636     MenuOption->Handle      = gFormData->HiiHandle;
    637     MenuOption->ThisTag     = Statement;
    638     MenuOption->NestInStatement = NestIn;
    639     MenuOption->EntryNumber = *MenuItemCount;
    640 
    641     MenuOption->Sequence = Index;
    642 
    643     if ((Statement->Attribute & HII_DISPLAY_GRAYOUT) != 0) {
    644       MenuOption->GrayOut = TRUE;
    645     } else {
    646       MenuOption->GrayOut = FALSE;
    647     }
    648 
    649     if ((Statement->Attribute & HII_DISPLAY_LOCK) != 0 || (gFormData->Attribute & HII_DISPLAY_LOCK) != 0) {
    650       MenuOption->GrayOut = TRUE;
    651     }
    652 
    653     //
    654     // If the form or the question has the lock attribute, deal same as grayout.
    655     //
    656     if ((gFormData->Attribute & HII_DISPLAY_LOCK) != 0 || (Statement->Attribute & HII_DISPLAY_LOCK) != 0) {
    657       MenuOption->GrayOut = TRUE;
    658     }
    659 
    660     switch (Statement->OpCode->OpCode) {
    661     case EFI_IFR_ORDERED_LIST_OP:
    662     case EFI_IFR_ONE_OF_OP:
    663     case EFI_IFR_NUMERIC_OP:
    664     case EFI_IFR_TIME_OP:
    665     case EFI_IFR_DATE_OP:
    666     case EFI_IFR_CHECKBOX_OP:
    667     case EFI_IFR_PASSWORD_OP:
    668     case EFI_IFR_STRING_OP:
    669       //
    670       // User could change the value of these items
    671       //
    672       MenuOption->IsQuestion = TRUE;
    673       break;
    674     case EFI_IFR_TEXT_OP:
    675       if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
    676         //
    677         // Initializing GrayOut option as TRUE for Text setup options
    678         // so that those options will be Gray in colour and un selectable.
    679         //
    680         MenuOption->GrayOut = TRUE;
    681       }
    682       break;
    683     default:
    684       MenuOption->IsQuestion = FALSE;
    685       break;
    686     }
    687 
    688     if ((Statement->Attribute & HII_DISPLAY_READONLY) != 0) {
    689       MenuOption->ReadOnly = TRUE;
    690       if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) {
    691         MenuOption->GrayOut = TRUE;
    692       }
    693     }
    694 
    695     if (Index == 0 &&
    696       (Statement->OpCode->OpCode != EFI_IFR_DATE_OP) &&
    697       (Statement->OpCode->OpCode != EFI_IFR_TIME_OP)) {
    698       Width  = GetWidth (MenuOption, NULL);
    699       for (; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth,&ArrayEntry, &OutputString) != 0x0000;) {
    700         //
    701         // If there is more string to process print on the next row and increment the Skip value
    702         //
    703         if (StrLen (&MenuOption->Description[ArrayEntry]) != 0) {
    704           NumberOfLines++;
    705         }
    706         FreePool (OutputString);
    707       }
    708     } else {
    709       //
    710       // Add three MenuOptions for Date/Time
    711       // Data format :      [01/02/2004]      [11:22:33]
    712       // Line number :        0  0    1         0  0  1
    713       //
    714       NumberOfLines = 0;
    715     }
    716 
    717     if (Index == 2) {
    718       //
    719       // Override LineNumber for the MenuOption in Date/Time sequence
    720       //
    721       MenuOption->Skip = 1;
    722     } else {
    723       MenuOption->Skip = NumberOfLines;
    724     }
    725 
    726     InsertTailList (&gMenuOption, &MenuOption->Link);
    727   }
    728 
    729   (*MenuItemCount)++;
    730 }
    731 
    732 /**
    733   Create the menu list base on the form data info.
    734 
    735 **/
    736 VOID
    737 ConvertStatementToMenu (
    738   VOID
    739   )
    740 {
    741   UINT16                        MenuItemCount;
    742   LIST_ENTRY                    *Link;
    743   LIST_ENTRY                    *NestLink;
    744   FORM_DISPLAY_ENGINE_STATEMENT *Statement;
    745   FORM_DISPLAY_ENGINE_STATEMENT *NestStatement;
    746 
    747   MenuItemCount = 0;
    748   InitializeListHead (&gMenuOption);
    749 
    750   Link = GetFirstNode (&gFormData->StatementListHead);
    751   while (!IsNull (&gFormData->StatementListHead, Link)) {
    752     Statement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (Link);
    753     Link = GetNextNode (&gFormData->StatementListHead, Link);
    754 
    755     //
    756     // Skip the opcode not recognized by Display core.
    757     //
    758     if (Statement->OpCode->OpCode == EFI_IFR_GUID_OP) {
    759       continue;
    760     }
    761 
    762     UiAddMenuOption (Statement, &MenuItemCount, FALSE);
    763 
    764     //
    765     // Check the statement nest in this host statement.
    766     //
    767     NestLink = GetFirstNode (&Statement->NestStatementList);
    768     while (!IsNull (&Statement->NestStatementList, NestLink)) {
    769       NestStatement = FORM_DISPLAY_ENGINE_STATEMENT_FROM_LINK (NestLink);
    770       NestLink = GetNextNode (&Statement->NestStatementList, NestLink);
    771 
    772       //
    773       // Skip the opcode not recognized by Display core.
    774       //
    775       if (NestStatement->OpCode->OpCode == EFI_IFR_GUID_OP) {
    776         continue;
    777       }
    778 
    779       UiAddMenuOption (NestStatement, &MenuItemCount, TRUE);
    780     }
    781   }
    782 }
    783 
    784 /**
    785   Count the storage space of a Unicode string.
    786 
    787   This function handles the Unicode string with NARROW_CHAR
    788   and WIDE_CHAR control characters. NARROW_HCAR and WIDE_CHAR
    789   does not count in the resultant output. If a WIDE_CHAR is
    790   hit, then 2 Unicode character will consume an output storage
    791   space with size of CHAR16 till a NARROW_CHAR is hit.
    792 
    793   If String is NULL, then ASSERT ().
    794 
    795   @param String          The input string to be counted.
    796 
    797   @return Storage space for the input string.
    798 
    799 **/
    800 UINTN
    801 GetStringWidth (
    802   IN CHAR16               *String
    803   )
    804 {
    805   UINTN Index;
    806   UINTN Count;
    807   UINTN IncrementValue;
    808 
    809   ASSERT (String != NULL);
    810   if (String == NULL) {
    811     return 0;
    812   }
    813 
    814   Index           = 0;
    815   Count           = 0;
    816   IncrementValue  = 1;
    817 
    818   do {
    819     //
    820     // Advance to the null-terminator or to the first width directive
    821     //
    822     for (;
    823          (String[Index] != NARROW_CHAR) && (String[Index] != WIDE_CHAR) && (String[Index] != 0);
    824          Index++, Count = Count + IncrementValue
    825         )
    826       ;
    827 
    828     //
    829     // We hit the null-terminator, we now have a count
    830     //
    831     if (String[Index] == 0) {
    832       break;
    833     }
    834     //
    835     // We encountered a narrow directive - strip it from the size calculation since it doesn't get printed
    836     // and also set the flag that determines what we increment by.(if narrow, increment by 1, if wide increment by 2)
    837     //
    838     if (String[Index] == NARROW_CHAR) {
    839       //
    840       // Skip to the next character
    841       //
    842       Index++;
    843       IncrementValue = 1;
    844     } else {
    845       //
    846       // Skip to the next character
    847       //
    848       Index++;
    849       IncrementValue = 2;
    850     }
    851   } while (String[Index] != 0);
    852 
    853   //
    854   // Increment by one to include the null-terminator in the size
    855   //
    856   Count++;
    857 
    858   return Count * sizeof (CHAR16);
    859 }
    860 
    861 /**
    862   Base on the input option string to update the skip value for a menu option.
    863 
    864   @param  MenuOption             The MenuOption to be checked.
    865   @param  OptionString           The input option string.
    866 
    867 **/
    868 VOID
    869 UpdateSkipInfoForMenu (
    870   IN UI_MENU_OPTION               *MenuOption,
    871   IN CHAR16                       *OptionString
    872   )
    873 {
    874   UINTN   Index;
    875   UINT16  Width;
    876   UINTN   Row;
    877   CHAR16  *OutputString;
    878   UINT16  GlyphWidth;
    879 
    880   Width         = (UINT16) gOptionBlockWidth;
    881   GlyphWidth    = 1;
    882   Row           = 1;
    883 
    884   for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
    885     if (StrLen (&OptionString[Index]) != 0) {
    886       Row++;
    887     }
    888 
    889     FreePool (OutputString);
    890   }
    891 
    892   if ((Row > MenuOption->Skip) &&
    893       (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_DATE_OP) &&
    894       (MenuOption->ThisTag->OpCode->OpCode != EFI_IFR_TIME_OP)) {
    895     MenuOption->Skip = Row;
    896   }
    897 }
    898 
    899 /**
    900   Update display lines for a Menu Option.
    901 
    902   @param  MenuOption             The MenuOption to be checked.
    903 
    904 **/
    905 VOID
    906 UpdateOptionSkipLines (
    907   IN UI_MENU_OPTION               *MenuOption
    908   )
    909 {
    910   CHAR16  *OptionString;
    911 
    912   OptionString  = NULL;
    913 
    914   ProcessOptions (MenuOption, FALSE, &OptionString, TRUE);
    915   if (OptionString != NULL) {
    916     UpdateSkipInfoForMenu (MenuOption, OptionString);
    917 
    918     FreePool (OptionString);
    919   }
    920 
    921   if ((MenuOption->ThisTag->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
    922     OptionString   = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
    923 
    924     if (OptionString != NULL) {
    925       UpdateSkipInfoForMenu (MenuOption, OptionString);
    926 
    927       FreePool (OptionString);
    928     }
    929   }
    930 }
    931 
    932 /**
    933   Check whether this Menu Option could be print.
    934 
    935   Check Prompt string, option string or text two string not NULL.
    936 
    937   This is an internal function.
    938 
    939   @param  MenuOption             The MenuOption to be checked.
    940 
    941   @retval TRUE                   This Menu Option is printable.
    942   @retval FALSE                  This Menu Option could not be printable.
    943 
    944 **/
    945 BOOLEAN
    946 PrintableMenu (
    947   UI_MENU_OPTION   *MenuOption
    948   )
    949 {
    950   EFI_STATUS    Status;
    951   EFI_STRING    OptionString;
    952 
    953   OptionString = NULL;
    954 
    955   if (MenuOption->Description[0] != '\0') {
    956     return TRUE;
    957   }
    958 
    959   Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
    960   if (EFI_ERROR (Status)) {
    961     return FALSE;
    962   }
    963   if (OptionString != NULL && OptionString[0] != '\0') {
    964     FreePool (OptionString);
    965     return TRUE;
    966   }
    967 
    968   if ((MenuOption->ThisTag->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo != 0)) {
    969     OptionString   = GetToken (((EFI_IFR_TEXT*)MenuOption->ThisTag->OpCode)->TextTwo, gFormData->HiiHandle);
    970     ASSERT (OptionString != NULL);
    971     if (OptionString[0] != '\0'){
    972       FreePool (OptionString);
    973       return TRUE;
    974     }
    975   }
    976 
    977   return FALSE;
    978 }
    979 
    980 /**
    981   Check whether this Menu Option could be highlighted.
    982 
    983   This is an internal function.
    984 
    985   @param  MenuOption             The MenuOption to be checked.
    986 
    987   @retval TRUE                   This Menu Option is selectable.
    988   @retval FALSE                  This Menu Option could not be selected.
    989 
    990 **/
    991 BOOLEAN
    992 IsSelectable (
    993   UI_MENU_OPTION   *MenuOption
    994   )
    995 {
    996   if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
    997       MenuOption->GrayOut || MenuOption->ReadOnly || !PrintableMenu (MenuOption)) {
    998     return FALSE;
    999   } else {
   1000     return TRUE;
   1001   }
   1002 }
   1003 
   1004 /**
   1005   Move to next selectable statement.
   1006 
   1007   This is an internal function.
   1008 
   1009   @param  GoUp                   The navigation direction. TRUE: up, FALSE: down.
   1010   @param  CurrentPosition        Current position.
   1011   @param  GapToTop               Gap position to top or bottom.
   1012   @param  FindInForm             Whether find menu in current form or beyond.
   1013 
   1014   @return The row distance from current MenuOption to next selectable MenuOption.
   1015 
   1016   @retval -1       Reach the begin of the menu, still can't find the selectable menu.
   1017   @retval Value    Find the selectable menu, maybe the truly selectable, maybe the
   1018                    first menu showing beyond current form or last menu showing in
   1019                    current form.
   1020                    The value is the line number between the new selected menu and the
   1021                    current select menu, not include the new selected menu.
   1022 
   1023 **/
   1024 INTN
   1025 MoveToNextStatement (
   1026   IN     BOOLEAN                   GoUp,
   1027   IN OUT LIST_ENTRY                **CurrentPosition,
   1028   IN     UINTN                     GapToTop,
   1029   IN     BOOLEAN                   FindInForm
   1030   )
   1031 {
   1032   INTN             Distance;
   1033   LIST_ENTRY       *Pos;
   1034   UI_MENU_OPTION   *NextMenuOption;
   1035   UI_MENU_OPTION   *PreMenuOption;
   1036 
   1037   Distance      = 0;
   1038   Pos           = *CurrentPosition;
   1039 
   1040   if (Pos == &gMenuOption) {
   1041     return -1;
   1042   }
   1043 
   1044   PreMenuOption = MENU_OPTION_FROM_LINK (Pos);
   1045 
   1046   while (TRUE) {
   1047     NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
   1048     //
   1049     // NextMenuOption->Row == 0 means this menu has not calculate
   1050     // the NextMenuOption->Skip value yet, just calculate here.
   1051     //
   1052     if (NextMenuOption->Row == 0) {
   1053       UpdateOptionSkipLines (NextMenuOption);
   1054     }
   1055 
   1056     //
   1057     // Check whether the menu is beyond current showing form,
   1058     // return the first one beyond the showing form.
   1059     //
   1060     if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
   1061       if (FindInForm) {
   1062         NextMenuOption = PreMenuOption;
   1063       }
   1064       break;
   1065     }
   1066 
   1067     //
   1068     // return the selectable menu in the showing form.
   1069     //
   1070     if (IsSelectable (NextMenuOption)) {
   1071       break;
   1072     }
   1073 
   1074     Distance += NextMenuOption->Skip;
   1075 
   1076     //
   1077     // Arrive at begin of the menu list.
   1078     //
   1079     if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
   1080       Distance = -1;
   1081       break;
   1082     }
   1083 
   1084     Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
   1085     PreMenuOption = NextMenuOption;
   1086   }
   1087 
   1088   *CurrentPosition = &NextMenuOption->Link;
   1089   return Distance;
   1090 }
   1091 
   1092 
   1093 /**
   1094   Process option string for date/time opcode.
   1095 
   1096   @param  MenuOption              Menu option point to date/time.
   1097   @param  OptionString            Option string input for process.
   1098   @param  AddOptCol               Whether need to update MenuOption->OptCol.
   1099 
   1100 **/
   1101 VOID
   1102 ProcessStringForDateTime (
   1103   UI_MENU_OPTION                  *MenuOption,
   1104   CHAR16                          *OptionString,
   1105   BOOLEAN                         AddOptCol
   1106   )
   1107 {
   1108   UINTN Index;
   1109   UINTN Count;
   1110   FORM_DISPLAY_ENGINE_STATEMENT          *Statement;
   1111   EFI_IFR_DATE                           *Date;
   1112   EFI_IFR_TIME                           *Time;
   1113 
   1114   ASSERT (MenuOption != NULL && OptionString != NULL);
   1115 
   1116   Statement = MenuOption->ThisTag;
   1117   Date      = NULL;
   1118   Time      = NULL;
   1119   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
   1120     Date = (EFI_IFR_DATE *) Statement->OpCode;
   1121   } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
   1122     Time = (EFI_IFR_TIME *) Statement->OpCode;
   1123   }
   1124 
   1125   //
   1126   // If leading spaces on OptionString - remove the spaces
   1127   //
   1128   for (Index = 0; OptionString[Index] == L' '; Index++) {
   1129     //
   1130     // Base on the blockspace to get the option column info.
   1131     //
   1132     if (AddOptCol) {
   1133       MenuOption->OptCol++;
   1134     }
   1135   }
   1136 
   1137   for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
   1138     OptionString[Count] = OptionString[Index];
   1139     Count++;
   1140   }
   1141   OptionString[Count] = CHAR_NULL;
   1142 
   1143   //
   1144   // Enable to suppress field in the opcode base on the flag.
   1145   //
   1146   if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP) {
   1147     //
   1148     // OptionString format is: <**:  **: ****>
   1149     //                        |month|day|year|
   1150     //                          4     3    5
   1151     //
   1152     if ((Date->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
   1153       //
   1154       // At this point, only "<**:" in the optionstring.
   1155       // Clean the day's ** field, after clean, the format is "<  :"
   1156       //
   1157       SetUnicodeMem (&OptionString[1], 2, L' ');
   1158     } else if ((Date->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
   1159       //
   1160       // At this point, only "**:" in the optionstring.
   1161       // Clean the month's "**" field, after clean, the format is "  :"
   1162       //
   1163       SetUnicodeMem (&OptionString[0], 2, L' ');
   1164     } else if ((Date->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
   1165       //
   1166       // At this point, only "****>" in the optionstring.
   1167       // Clean the year's "****" field, after clean, the format is "  >"
   1168       //
   1169       SetUnicodeMem (&OptionString[0], 4, L' ');
   1170     }
   1171   } else if (Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
   1172     //
   1173     // OptionString format is: <**:  **:    **>
   1174     //                        |hour|minute|second|
   1175     //                          4     3      3
   1176     //
   1177     if ((Time->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
   1178       //
   1179       // At this point, only "<**:" in the optionstring.
   1180       // Clean the hour's ** field, after clean, the format is "<  :"
   1181       //
   1182       SetUnicodeMem (&OptionString[1], 2, L' ');
   1183     } else if ((Time->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
   1184       //
   1185       // At this point, only "**:" in the optionstring.
   1186       // Clean the minute's "**" field, after clean, the format is "  :"
   1187       //
   1188       SetUnicodeMem (&OptionString[0], 2, L' ');
   1189     } else if ((Time->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
   1190       //
   1191       // At this point, only "**>" in the optionstring.
   1192       // Clean the second's "**" field, after clean, the format is "  >"
   1193       //
   1194       SetUnicodeMem (&OptionString[0], 2, L' ');
   1195     }
   1196   }
   1197 }
   1198 
   1199 
   1200 /**
   1201   Adjust Data and Time position accordingly.
   1202   Data format :      [01/02/2004]      [11:22:33]
   1203   Line number :        0  0    1         0  0  1
   1204 
   1205   This is an internal function.
   1206 
   1207   @param  DirectionUp            the up or down direction. False is down. True is
   1208                                  up.
   1209   @param  CurrentPosition        Current position. On return: Point to the last
   1210                                  Option (Year or Second) if up; Point to the first
   1211                                  Option (Month or Hour) if down.
   1212 
   1213   @return Return line number to pad. It is possible that we stand on a zero-advance
   1214   @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
   1215 
   1216 **/
   1217 UINTN
   1218 AdjustDateAndTimePosition (
   1219   IN     BOOLEAN                     DirectionUp,
   1220   IN OUT LIST_ENTRY                  **CurrentPosition
   1221   )
   1222 {
   1223   UINTN           Count;
   1224   LIST_ENTRY      *NewPosition;
   1225   UI_MENU_OPTION  *MenuOption;
   1226   UINTN           PadLineNumber;
   1227 
   1228   PadLineNumber = 0;
   1229   NewPosition   = *CurrentPosition;
   1230   MenuOption    = MENU_OPTION_FROM_LINK (NewPosition);
   1231 
   1232   if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) ||
   1233       (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
   1234     //
   1235     // Calculate the distance from current position to the last Date/Time MenuOption
   1236     //
   1237     Count = 0;
   1238     while (MenuOption->Skip == 0) {
   1239       Count++;
   1240       NewPosition   = NewPosition->ForwardLink;
   1241       MenuOption    = MENU_OPTION_FROM_LINK (NewPosition);
   1242       PadLineNumber = 1;
   1243     }
   1244 
   1245     NewPosition = *CurrentPosition;
   1246     if (DirectionUp) {
   1247       //
   1248       // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
   1249       // to be one that back to the previous set of MenuOptions, we need to advance to the first
   1250       // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
   1251       // checking can be done.
   1252       //
   1253       while (Count++ < 2) {
   1254         NewPosition = NewPosition->BackLink;
   1255       }
   1256     } else {
   1257       //
   1258       // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
   1259       // to be one that progresses to the next set of MenuOptions, we need to advance to the last
   1260       // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
   1261       // checking can be done.
   1262       //
   1263       while (Count-- > 0) {
   1264         NewPosition = NewPosition->ForwardLink;
   1265       }
   1266     }
   1267 
   1268     *CurrentPosition = NewPosition;
   1269   }
   1270 
   1271   return PadLineNumber;
   1272 }
   1273 
   1274 /**
   1275   Get step info from numeric opcode.
   1276 
   1277   @param[in] OpCode     The input numeric op code.
   1278 
   1279   @return step info for this opcode.
   1280 **/
   1281 UINT64
   1282 GetFieldFromNum (
   1283   IN  EFI_IFR_OP_HEADER     *OpCode
   1284   )
   1285 {
   1286   EFI_IFR_NUMERIC       *NumericOp;
   1287   UINT64                Step;
   1288 
   1289   NumericOp = (EFI_IFR_NUMERIC *) OpCode;
   1290 
   1291   switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
   1292   case EFI_IFR_NUMERIC_SIZE_1:
   1293     Step    = NumericOp->data.u8.Step;
   1294     break;
   1295 
   1296   case EFI_IFR_NUMERIC_SIZE_2:
   1297     Step    = NumericOp->data.u16.Step;
   1298     break;
   1299 
   1300   case EFI_IFR_NUMERIC_SIZE_4:
   1301     Step    = NumericOp->data.u32.Step;
   1302     break;
   1303 
   1304   case EFI_IFR_NUMERIC_SIZE_8:
   1305     Step    = NumericOp->data.u64.Step;
   1306     break;
   1307 
   1308   default:
   1309     Step = 0;
   1310     break;
   1311   }
   1312 
   1313   return Step;
   1314 }
   1315 
   1316 /**
   1317   Find the registered HotKey based on KeyData.
   1318 
   1319   @param[in] KeyData     A pointer to a buffer that describes the keystroke
   1320                          information for the hot key.
   1321 
   1322   @return The registered HotKey context. If no found, NULL will return.
   1323 **/
   1324 BROWSER_HOT_KEY *
   1325 GetHotKeyFromRegisterList (
   1326   IN EFI_INPUT_KEY *KeyData
   1327   )
   1328 {
   1329   LIST_ENTRY       *Link;
   1330   BROWSER_HOT_KEY  *HotKey;
   1331 
   1332   Link = GetFirstNode (&gFormData->HotKeyListHead);
   1333   while (!IsNull (&gFormData->HotKeyListHead, Link)) {
   1334     HotKey = BROWSER_HOT_KEY_FROM_LINK (Link);
   1335 
   1336     if (HotKey->KeyData->ScanCode == KeyData->ScanCode) {
   1337       return HotKey;
   1338     }
   1339 
   1340     Link = GetNextNode (&gFormData->HotKeyListHead, Link);
   1341   }
   1342 
   1343   return NULL;
   1344 }
   1345 
   1346 
   1347 /**
   1348   Determine if the menu is the last menu that can be selected.
   1349 
   1350   This is an internal function.
   1351 
   1352   @param  Direction              The scroll direction. False is down. True is up.
   1353   @param  CurrentPos             The current focus.
   1354 
   1355   @return FALSE -- the menu isn't the last menu that can be selected.
   1356   @return TRUE  -- the menu is the last menu that can be selected.
   1357 
   1358 **/
   1359 BOOLEAN
   1360 ValueIsScroll (
   1361   IN  BOOLEAN                     Direction,
   1362   IN  LIST_ENTRY                  *CurrentPos
   1363   )
   1364 {
   1365   LIST_ENTRY      *Temp;
   1366 
   1367   Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
   1368 
   1369   if (Temp == &gMenuOption) {
   1370     return TRUE;
   1371   }
   1372 
   1373   return FALSE;
   1374 }
   1375 
   1376 /**
   1377   Wait for a given event to fire, or for an optional timeout to expire.
   1378 
   1379   @param  Event                  The event to wait for
   1380 
   1381   @retval UI_EVENT_TYPE          The type of the event which is trigged.
   1382 
   1383 **/
   1384 UI_EVENT_TYPE
   1385 UiWaitForEvent (
   1386   IN EFI_EVENT                Event
   1387   )
   1388 {
   1389   EFI_STATUS  Status;
   1390   UINTN       Index;
   1391   UINTN       EventNum;
   1392   UINT64      Timeout;
   1393   EFI_EVENT   TimerEvent;
   1394   EFI_EVENT   WaitList[3];
   1395   UI_EVENT_TYPE  EventType;
   1396 
   1397   TimerEvent = NULL;
   1398   Timeout    = FormExitTimeout(gFormData);
   1399 
   1400   if (Timeout != 0) {
   1401     Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
   1402 
   1403     //
   1404     // Set the timer event
   1405     //
   1406     gBS->SetTimer (
   1407           TimerEvent,
   1408           TimerRelative,
   1409           Timeout
   1410           );
   1411   }
   1412 
   1413   WaitList[0] = Event;
   1414   EventNum    = 1;
   1415   if (gFormData->FormRefreshEvent != NULL) {
   1416     WaitList[EventNum] = gFormData->FormRefreshEvent;
   1417     EventNum ++;
   1418   }
   1419 
   1420   if (Timeout != 0) {
   1421     WaitList[EventNum] = TimerEvent;
   1422     EventNum ++;
   1423   }
   1424 
   1425   Status = gBS->WaitForEvent (EventNum, WaitList, &Index);
   1426   ASSERT_EFI_ERROR (Status);
   1427 
   1428   switch (Index) {
   1429   case 0:
   1430    EventType = UIEventKey;
   1431    break;
   1432 
   1433   case 1:
   1434     if (gFormData->FormRefreshEvent != NULL) {
   1435       EventType = UIEventDriver;
   1436     } else {
   1437       ASSERT (Timeout != 0 && EventNum == 2);
   1438       EventType = UIEventTimeOut;
   1439     }
   1440     break;
   1441 
   1442   default:
   1443     ASSERT (Index == 2 && EventNum == 3);
   1444     EventType = UIEventTimeOut;
   1445     break;
   1446   }
   1447 
   1448   if (Timeout != 0) {
   1449     gBS->CloseEvent (TimerEvent);
   1450   }
   1451 
   1452   return EventType;
   1453 }
   1454 
   1455 /**
   1456   Get question id info from the input opcode header.
   1457 
   1458   @param  OpCode                 The input opcode header pointer.
   1459 
   1460   @retval                        The question id for this opcode.
   1461 
   1462 **/
   1463 EFI_QUESTION_ID
   1464 GetQuestionIdInfo (
   1465   IN   EFI_IFR_OP_HEADER     *OpCode
   1466   )
   1467 {
   1468   EFI_IFR_QUESTION_HEADER   *QuestionHeader;
   1469 
   1470   if (OpCode->Length < sizeof (EFI_IFR_OP_HEADER) + sizeof (EFI_IFR_QUESTION_HEADER)) {
   1471     return 0;
   1472   }
   1473 
   1474   QuestionHeader = (EFI_IFR_QUESTION_HEADER *)((UINT8 *) OpCode + sizeof(EFI_IFR_OP_HEADER));
   1475 
   1476   return QuestionHeader->QuestionId;
   1477 }
   1478 
   1479 
   1480 /**
   1481   Find the top of screen menu base on the current menu.
   1482 
   1483   @param  CurPos                 Current input menu.
   1484   @param  Rows                   Totol screen rows.
   1485   @param  SkipValue              SkipValue for this new form.
   1486 
   1487   @retval TopOfScreen            Top of screen menu for the new form.
   1488 
   1489 **/
   1490 LIST_ENTRY *
   1491 FindTopOfScreenMenu (
   1492   IN  LIST_ENTRY                      *CurPos,
   1493   IN  UINTN                           Rows,
   1494   OUT UINTN                           *SkipValue
   1495   )
   1496 {
   1497   LIST_ENTRY        *Link;
   1498   LIST_ENTRY        *TopOfScreen;
   1499   UI_MENU_OPTION    *PreviousMenuOption;
   1500 
   1501   Link = CurPos;
   1502   PreviousMenuOption = NULL;
   1503 
   1504   while (Link->BackLink != &gMenuOption) {
   1505     Link = Link->BackLink;
   1506     PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
   1507     if (PreviousMenuOption->Row == 0) {
   1508       UpdateOptionSkipLines (PreviousMenuOption);
   1509     }
   1510     if (Rows <= PreviousMenuOption->Skip) {
   1511       break;
   1512     }
   1513     Rows = Rows - PreviousMenuOption->Skip;
   1514   }
   1515 
   1516   if (Link->BackLink == &gMenuOption) {
   1517     TopOfScreen = gMenuOption.ForwardLink;
   1518     if (PreviousMenuOption != NULL && Rows < PreviousMenuOption->Skip) {
   1519       *SkipValue = PreviousMenuOption->Skip - Rows;
   1520     } else {
   1521       *SkipValue = 0;
   1522     }
   1523   } else {
   1524     TopOfScreen = Link;
   1525     *SkipValue = PreviousMenuOption->Skip - Rows;
   1526   }
   1527 
   1528   return TopOfScreen;
   1529 }
   1530 
   1531 /**
   1532   Get the index info for this opcode.
   1533 
   1534   @param  OpCode      The input opcode for the statement.
   1535 
   1536   @retval  The index of this statement.
   1537 
   1538 **/
   1539 UINTN
   1540 GetIndexInfoForOpcode (
   1541   IN EFI_IFR_OP_HEADER  *OpCode
   1542   )
   1543 {
   1544   LIST_ENTRY                      *NewPos;
   1545   UI_MENU_OPTION                  *MenuOption;
   1546   UINTN                           Index;
   1547 
   1548   NewPos = gMenuOption.ForwardLink;
   1549   Index  = 0;
   1550 
   1551   for (NewPos = gMenuOption.ForwardLink; NewPos != &gMenuOption; NewPos = NewPos->ForwardLink){
   1552     MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1553 
   1554     if (CompareMem (MenuOption->ThisTag->OpCode, OpCode, OpCode->Length) == 0) {
   1555       if (MenuOption->ThisTag->OpCode == OpCode) {
   1556         return Index;
   1557       }
   1558 
   1559       Index ++;
   1560     }
   1561   }
   1562 
   1563   return Index;
   1564 }
   1565 
   1566 /**
   1567   Is this the saved highlight statement.
   1568 
   1569   @param  HighLightedStatement      The input highlight statement.
   1570 
   1571   @retval  TRUE   This is the highlight statement.
   1572   @retval  FALSE  This is not the highlight statement.
   1573 
   1574 **/
   1575 BOOLEAN
   1576 IsSavedHighlightStatement (
   1577   IN FORM_DISPLAY_ENGINE_STATEMENT  *HighLightedStatement
   1578   )
   1579 {
   1580   if ((gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle) &&
   1581       (gFormData->FormId == gHighligthMenuInfo.FormId)) {
   1582     if (gHighligthMenuInfo.HLTQuestionId != 0) {
   1583       return (BOOLEAN) (gHighligthMenuInfo.HLTQuestionId == GetQuestionIdInfo (HighLightedStatement->OpCode));
   1584     } else {
   1585       if (CompareMem (gHighligthMenuInfo.HLTOpCode, HighLightedStatement->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) {
   1586         if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(HighLightedStatement->OpCode)) {
   1587           return TRUE;
   1588         } else {
   1589           return FALSE;
   1590         }
   1591       }
   1592     }
   1593   }
   1594 
   1595   return FALSE;
   1596 }
   1597 
   1598 /**
   1599   Is this the highlight menu.
   1600 
   1601   @param  MenuOption      The input Menu option.
   1602 
   1603   @retval  TRUE   This is the highlight menu option.
   1604   @retval  FALSE  This is not the highlight menu option.
   1605 
   1606 **/
   1607 BOOLEAN
   1608 IsHighLightMenuOption (
   1609   IN UI_MENU_OPTION     *MenuOption
   1610   )
   1611 {
   1612   if (gHighligthMenuInfo.HLTQuestionId != 0) {
   1613     if (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.HLTQuestionId) {
   1614       return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence);
   1615     }
   1616   } else {
   1617     if(CompareMem (gHighligthMenuInfo.HLTOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.HLTOpCode->Length) == 0) {
   1618       if (gHighligthMenuInfo.HLTIndex == 0 || gHighligthMenuInfo.HLTIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) {
   1619         return (BOOLEAN) (MenuOption->Sequence == gHighligthMenuInfo.HLTSequence);
   1620       } else {
   1621         return FALSE;
   1622       }
   1623     }
   1624   }
   1625 
   1626   return FALSE;
   1627 }
   1628 
   1629 /**
   1630   Find the highlight menu.
   1631 
   1632   If the input is NULL, base on the record highlight info in
   1633   gHighligthMenuInfo to find the last highlight menu.
   1634 
   1635   @param  HighLightedStatement      The input highlight statement.
   1636 
   1637   @retval  The highlight menu index.
   1638 
   1639 **/
   1640 LIST_ENTRY *
   1641 FindHighLightMenuOption (
   1642  IN FORM_DISPLAY_ENGINE_STATEMENT  *HighLightedStatement
   1643  )
   1644 {
   1645   LIST_ENTRY                      *NewPos;
   1646   UI_MENU_OPTION                  *MenuOption;
   1647 
   1648   NewPos = gMenuOption.ForwardLink;
   1649   MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1650 
   1651   if (HighLightedStatement != NULL) {
   1652     while (MenuOption->ThisTag != HighLightedStatement) {
   1653       NewPos     = NewPos->ForwardLink;
   1654       if (NewPos == &gMenuOption) {
   1655         //
   1656         // Not Found it, break
   1657         //
   1658         break;
   1659       }
   1660       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1661     }
   1662 
   1663     //
   1664     // Must find the highlight statement.
   1665     //
   1666     ASSERT (NewPos != &gMenuOption);
   1667 
   1668   } else {
   1669     while (!IsHighLightMenuOption (MenuOption)) {
   1670       NewPos     = NewPos->ForwardLink;
   1671       if (NewPos == &gMenuOption) {
   1672         //
   1673         // Not Found it, break
   1674         //
   1675         break;
   1676       }
   1677       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1678     }
   1679 
   1680     //
   1681     // Highlight statement has disappear (suppressed/disableed)
   1682     //
   1683     if (NewPos == &gMenuOption) {
   1684       NewPos = NULL;
   1685     }
   1686   }
   1687 
   1688   return NewPos;
   1689 }
   1690 
   1691 /**
   1692   Is this the Top of screen menu.
   1693 
   1694   @param  MenuOption      The input Menu option.
   1695 
   1696   @retval  TRUE   This is the Top of screen menu option.
   1697   @retval  FALSE  This is not the Top of screen menu option.
   1698 
   1699 **/
   1700 BOOLEAN
   1701 IsTopOfScreeMenuOption (
   1702   IN UI_MENU_OPTION     *MenuOption
   1703   )
   1704 {
   1705   if (gHighligthMenuInfo.TOSQuestionId != 0) {
   1706     return (BOOLEAN) (GetQuestionIdInfo(MenuOption->ThisTag->OpCode) == gHighligthMenuInfo.TOSQuestionId);
   1707   }
   1708 
   1709   if(CompareMem (gHighligthMenuInfo.TOSOpCode, MenuOption->ThisTag->OpCode, gHighligthMenuInfo.TOSOpCode->Length) == 0) {
   1710     if (gHighligthMenuInfo.TOSIndex == 0 || gHighligthMenuInfo.TOSIndex == GetIndexInfoForOpcode(MenuOption->ThisTag->OpCode)) {
   1711       return TRUE;
   1712     } else {
   1713       return FALSE;
   1714     }
   1715   }
   1716 
   1717   return FALSE;
   1718 }
   1719 
   1720 /**
   1721   Find the Top of screen menu.
   1722 
   1723   If the input is NULL, base on the record highlight info in
   1724   gHighligthMenuInfo to find the last highlight menu.
   1725 
   1726   @param  HighLightedStatement      The input highlight statement.
   1727 
   1728   @retval  The highlight menu index.
   1729 
   1730 **/
   1731 LIST_ENTRY *
   1732 FindTopOfScreenMenuOption (
   1733  VOID
   1734  )
   1735 {
   1736   LIST_ENTRY                      *NewPos;
   1737   UI_MENU_OPTION                  *MenuOption;
   1738 
   1739   NewPos = gMenuOption.ForwardLink;
   1740   MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1741 
   1742   while (!IsTopOfScreeMenuOption(MenuOption)) {
   1743     NewPos     = NewPos->ForwardLink;
   1744     if (NewPos == &gMenuOption) {
   1745       //
   1746       // Not Found it, break
   1747       //
   1748       break;
   1749     }
   1750     MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   1751   }
   1752 
   1753   //
   1754   // Last time top of screen menu has disappeared.
   1755   //
   1756   if (NewPos == &gMenuOption) {
   1757     NewPos = NULL;
   1758   }
   1759 
   1760   return NewPos;
   1761 }
   1762 
   1763 /**
   1764   Find the first menu which will be show at the top.
   1765 
   1766   @param  FormData               The data info for this form.
   1767   @param  TopOfScreen            The link_entry pointer to top menu.
   1768   @param  HighlightMenu          The menu which will be highlight.
   1769   @param  SkipValue              The skip value for the top menu.
   1770 
   1771 **/
   1772 VOID
   1773 FindTopMenu (
   1774   IN  FORM_DISPLAY_ENGINE_FORM  *FormData,
   1775   OUT LIST_ENTRY                **TopOfScreen,
   1776   OUT LIST_ENTRY                **HighlightMenu,
   1777   OUT UINTN                     *SkipValue
   1778   )
   1779 {
   1780   UINTN                           TopRow;
   1781   UINTN                           BottomRow;
   1782   UI_MENU_OPTION                  *MenuOption;
   1783   UINTN                           TmpValue;
   1784 
   1785   TopRow    = gStatementDimensions.TopRow    + SCROLL_ARROW_HEIGHT;
   1786   BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT;
   1787   //
   1788   // When option mismatch happens,there exist two cases,one is reenter the form, just like the if case below,
   1789   // and the other is exit current form and enter last form, it can be covered by the else case.
   1790   //
   1791   if (gMisMatch && gFormData->HiiHandle == gHighligthMenuInfo.HiiHandle && gFormData->FormId == gHighligthMenuInfo.FormId) {
   1792     //
   1793     // Reenter caused by option mismatch or auto exit caused by refresh form(refresh interval/guid),
   1794     // base on the record highlight info to find the highlight menu.
   1795     //
   1796 
   1797     *HighlightMenu = FindHighLightMenuOption(NULL);
   1798     if (*HighlightMenu != NULL) {
   1799       //
   1800       // Update skip info for this highlight menu.
   1801       //
   1802       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
   1803       UpdateOptionSkipLines (MenuOption);
   1804 
   1805       //
   1806       // Found the last time highlight menu.
   1807       //
   1808       *TopOfScreen = FindTopOfScreenMenuOption();
   1809       if (*TopOfScreen != NULL) {
   1810         //
   1811         // Found the last time selectable top of screen menu.
   1812         //
   1813         AdjustDateAndTimePosition(TRUE, TopOfScreen);
   1814         MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen);
   1815         UpdateOptionSkipLines (MenuOption);
   1816 
   1817         *SkipValue = gHighligthMenuInfo.SkipValue;
   1818       } else {
   1819         //
   1820         // Not found last time top of screen menu, so base on current highlight menu
   1821         // to find the new top of screen menu.
   1822         // Make the current highlight menu at the bottom of the form to calculate the
   1823         // top of screen menu.
   1824         //
   1825         if (MenuOption->Skip >= BottomRow - TopRow) {
   1826           *TopOfScreen = *HighlightMenu;
   1827           TmpValue     = 0;
   1828         } else {
   1829           *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
   1830         }
   1831 
   1832         *SkipValue   = TmpValue;
   1833       }
   1834     } else {
   1835       //
   1836       // Last time highlight menu has disappear, find the first highlightable menu as the default one.
   1837       //
   1838       *HighlightMenu = gMenuOption.ForwardLink;
   1839       if (!IsListEmpty (&gMenuOption)) {
   1840         MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE);
   1841       }
   1842       *TopOfScreen   = gMenuOption.ForwardLink;
   1843       *SkipValue = 0;
   1844     }
   1845 
   1846   } else if (FormData->HighLightedStatement != NULL) {
   1847     if (IsSavedHighlightStatement (FormData->HighLightedStatement)) {
   1848       //
   1849       // Input highlight menu is same as last time highlight menu.
   1850       // Base on last time highlight menu to set the top of screen menu and highlight menu.
   1851       //
   1852       *HighlightMenu = FindHighLightMenuOption(NULL);
   1853       ASSERT (*HighlightMenu != NULL);
   1854 
   1855       //
   1856       // Update skip info for this highlight menu.
   1857       //
   1858       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
   1859       UpdateOptionSkipLines (MenuOption);
   1860 
   1861       *TopOfScreen = FindTopOfScreenMenuOption();
   1862       if (*TopOfScreen == NULL) {
   1863         //
   1864         // Not found last time top of screen menu, so base on current highlight menu
   1865         // to find the new top of screen menu.
   1866         // Make the current highlight menu at the bottom of the form to calculate the
   1867         // top of screen menu.
   1868         //
   1869         if (MenuOption->Skip >= BottomRow - TopRow) {
   1870           *TopOfScreen = *HighlightMenu;
   1871           TmpValue     = 0;
   1872         } else {
   1873           *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
   1874         }
   1875 
   1876         *SkipValue   = TmpValue;
   1877       } else {
   1878         AdjustDateAndTimePosition(TRUE, TopOfScreen);
   1879         MenuOption = MENU_OPTION_FROM_LINK (*TopOfScreen);
   1880         UpdateOptionSkipLines (MenuOption);
   1881 
   1882         *SkipValue = gHighligthMenuInfo.SkipValue;
   1883       }
   1884       AdjustDateAndTimePosition(TRUE, TopOfScreen);
   1885     } else {
   1886       //
   1887       // Input highlight menu is not save as last time highlight menu.
   1888       //
   1889       *HighlightMenu = FindHighLightMenuOption(FormData->HighLightedStatement);
   1890       MenuOption = MENU_OPTION_FROM_LINK (*HighlightMenu);
   1891       UpdateOptionSkipLines (MenuOption);
   1892 
   1893       //
   1894       // Make the current highlight menu at the bottom of the form to calculate the
   1895       // top of screen menu.
   1896       //
   1897       if (MenuOption->Skip >= BottomRow - TopRow) {
   1898         *TopOfScreen = *HighlightMenu;
   1899         TmpValue     = 0;
   1900       } else {
   1901         *TopOfScreen = FindTopOfScreenMenu(*HighlightMenu, BottomRow - TopRow - MenuOption->Skip, &TmpValue);
   1902       }
   1903 
   1904       *SkipValue   = TmpValue;
   1905     }
   1906     AdjustDateAndTimePosition(TRUE, TopOfScreen);
   1907   } else {
   1908     //
   1909     // If not has input highlight statement, just return the first one in this form.
   1910     //
   1911     *TopOfScreen   = gMenuOption.ForwardLink;
   1912     *HighlightMenu = gMenuOption.ForwardLink;
   1913     if (!IsListEmpty (&gMenuOption)) {
   1914       MoveToNextStatement (FALSE, HighlightMenu, BottomRow - TopRow, TRUE);
   1915     }
   1916     *SkipValue     = 0;
   1917   }
   1918 
   1919   gMisMatch = FALSE;
   1920 
   1921   //
   1922   // First enter to show the menu, update highlight info.
   1923   //
   1924   UpdateHighlightMenuInfo (*HighlightMenu, *TopOfScreen, *SkipValue);
   1925 }
   1926 
   1927 /**
   1928   Record the highlight menu and top of screen menu info.
   1929 
   1930   @param  Highlight               The menu opton which is highlight.
   1931   @param  TopOfScreen             The menu opton which is at the top of the form.
   1932   @param  SkipValue               The skip line info for the top of screen menu.
   1933 
   1934 **/
   1935 VOID
   1936 UpdateHighlightMenuInfo (
   1937   IN  LIST_ENTRY                      *Highlight,
   1938   IN  LIST_ENTRY                      *TopOfScreen,
   1939   IN  UINTN                           SkipValue
   1940   )
   1941 {
   1942   UI_MENU_OPTION                  *MenuOption;
   1943   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
   1944 
   1945   gHighligthMenuInfo.HiiHandle  = gFormData->HiiHandle;
   1946   gHighligthMenuInfo.FormId     = gFormData->FormId;
   1947   gHighligthMenuInfo.SkipValue  = (UINT16)SkipValue;
   1948 
   1949   if (!IsListEmpty (&gMenuOption)) {
   1950     MenuOption = MENU_OPTION_FROM_LINK (Highlight);
   1951     Statement  = MenuOption->ThisTag;
   1952 
   1953     gUserInput->SelectedStatement = Statement;
   1954 
   1955     gHighligthMenuInfo.HLTSequence   = MenuOption->Sequence;
   1956     gHighligthMenuInfo.HLTQuestionId = GetQuestionIdInfo(Statement->OpCode);
   1957     if (gHighligthMenuInfo.HLTQuestionId == 0) {
   1958       //
   1959       // if question id == 0, save the opcode buffer..
   1960       //
   1961       if (gHighligthMenuInfo.HLTOpCode != NULL) {
   1962         FreePool (gHighligthMenuInfo.HLTOpCode);
   1963       }
   1964       gHighligthMenuInfo.HLTOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode);
   1965       ASSERT (gHighligthMenuInfo.HLTOpCode != NULL);
   1966 
   1967       gHighligthMenuInfo.HLTIndex = GetIndexInfoForOpcode(Statement->OpCode);
   1968     }
   1969 
   1970     MenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
   1971     Statement  = MenuOption->ThisTag;
   1972 
   1973     gHighligthMenuInfo.TOSQuestionId = GetQuestionIdInfo(Statement->OpCode);
   1974     if (gHighligthMenuInfo.TOSQuestionId == 0) {
   1975       //
   1976       // if question id == 0, save the opcode buffer..
   1977       //
   1978       if (gHighligthMenuInfo.TOSOpCode != NULL) {
   1979         FreePool (gHighligthMenuInfo.TOSOpCode);
   1980       }
   1981       gHighligthMenuInfo.TOSOpCode = AllocateCopyPool (Statement->OpCode->Length, Statement->OpCode);
   1982       ASSERT (gHighligthMenuInfo.TOSOpCode != NULL);
   1983 
   1984       gHighligthMenuInfo.TOSIndex = GetIndexInfoForOpcode(Statement->OpCode);
   1985     }
   1986   } else {
   1987     gUserInput->SelectedStatement    = NULL;
   1988 
   1989     gHighligthMenuInfo.HLTSequence   = 0;
   1990     gHighligthMenuInfo.HLTQuestionId = 0;
   1991     if (gHighligthMenuInfo.HLTOpCode != NULL) {
   1992       FreePool (gHighligthMenuInfo.HLTOpCode);
   1993     }
   1994     gHighligthMenuInfo.HLTOpCode     = NULL;
   1995     gHighligthMenuInfo.HLTIndex      = 0;
   1996 
   1997     gHighligthMenuInfo.TOSQuestionId = 0;
   1998     if (gHighligthMenuInfo.TOSOpCode != NULL) {
   1999       FreePool (gHighligthMenuInfo.TOSOpCode);
   2000     }
   2001     gHighligthMenuInfo.TOSOpCode     = NULL;
   2002     gHighligthMenuInfo.TOSIndex      = 0;
   2003   }
   2004 }
   2005 
   2006 /**
   2007   Update attribut for this menu.
   2008 
   2009   @param  MenuOption               The menu opton which this attribut used to.
   2010   @param  Highlight                Whether this menu will be highlight.
   2011 
   2012 **/
   2013 VOID
   2014 SetDisplayAttribute (
   2015   IN UI_MENU_OPTION                  *MenuOption,
   2016   IN BOOLEAN                         Highlight
   2017   )
   2018 {
   2019   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
   2020 
   2021   Statement = MenuOption->ThisTag;
   2022 
   2023   if (Highlight) {
   2024     gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
   2025     return;
   2026   }
   2027 
   2028   if (MenuOption->GrayOut) {
   2029     gST->ConOut->SetAttribute (gST->ConOut, GetGrayedTextColor ());
   2030   } else {
   2031     if (Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) {
   2032       gST->ConOut->SetAttribute (gST->ConOut, GetSubTitleTextColor ());
   2033     } else {
   2034       gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
   2035     }
   2036   }
   2037 }
   2038 
   2039 /**
   2040   Print string for this menu option.
   2041 
   2042   @param  MenuOption               The menu opton which this attribut used to.
   2043   @param  Col                      The column that this string will be print at.
   2044   @param  Row                      The row that this string will be print at.
   2045   @param  String                   The string which need to print.
   2046   @param  Width                    The width need to print, if string is less than the
   2047                                    width, the block space will be used.
   2048   @param  Highlight                Whether this menu will be highlight.
   2049 
   2050 **/
   2051 VOID
   2052 DisplayMenuString (
   2053   IN UI_MENU_OPTION         *MenuOption,
   2054   IN UINTN                  Col,
   2055   IN UINTN                  Row,
   2056   IN CHAR16                 *String,
   2057   IN UINTN                  Width,
   2058   IN BOOLEAN                Highlight
   2059   )
   2060 {
   2061   UINTN            Length;
   2062 
   2063   //
   2064   // Print string with normal color.
   2065   //
   2066   if (!Highlight) {
   2067     PrintStringAtWithWidth (Col, Row, String, Width);
   2068     return;
   2069   }
   2070 
   2071   //
   2072   // Print the highlight menu string.
   2073   // First print the highlight string.
   2074   //
   2075   SetDisplayAttribute(MenuOption, TRUE);
   2076   Length = PrintStringAt (Col, Row, String);
   2077 
   2078   //
   2079   // Second, clean the empty after the string.
   2080   //
   2081   SetDisplayAttribute(MenuOption, FALSE);
   2082   PrintStringAtWithWidth (Col + Length, Row, L"", Width - Length);
   2083 }
   2084 
   2085 /**
   2086   Check whether this menu can has option string.
   2087 
   2088   @param  MenuOption               The menu opton which this attribut used to.
   2089 
   2090   @retval TRUE                     This menu option can have option string.
   2091   @retval FALSE                    This menu option can't have option string.
   2092 
   2093 **/
   2094 BOOLEAN
   2095 HasOptionString (
   2096   IN UI_MENU_OPTION                  *MenuOption
   2097   )
   2098 {
   2099   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
   2100   CHAR16                          *String;
   2101   UINTN                           Size;
   2102   EFI_IFR_TEXT                    *TestOp;
   2103 
   2104   Size = 0;
   2105   Statement = MenuOption->ThisTag;
   2106 
   2107   //
   2108   // See if the second text parameter is really NULL
   2109   //
   2110   if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
   2111     TestOp = (EFI_IFR_TEXT *) Statement->OpCode;
   2112     if (TestOp->TextTwo != 0) {
   2113       String = GetToken (TestOp->TextTwo, gFormData->HiiHandle);
   2114       Size   = StrLen (String);
   2115       FreePool (String);
   2116     }
   2117   }
   2118 
   2119   if ((Statement->OpCode->OpCode == EFI_IFR_SUBTITLE_OP) ||
   2120     (Statement->OpCode->OpCode == EFI_IFR_REF_OP) ||
   2121     (Statement->OpCode->OpCode == EFI_IFR_PASSWORD_OP) ||
   2122     (Statement->OpCode->OpCode == EFI_IFR_ACTION_OP) ||
   2123     (Statement->OpCode->OpCode == EFI_IFR_RESET_BUTTON_OP) ||
   2124     //
   2125     // Allow a wide display if text op-code and no secondary text op-code
   2126     //
   2127     ((Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) && (Size == 0))
   2128     ) {
   2129 
   2130     return FALSE;
   2131   }
   2132 
   2133   return TRUE;
   2134 }
   2135 
   2136 /**
   2137   Double confirm with user about the action.
   2138 
   2139   @param  Action               The user input action.
   2140 
   2141   @retval TRUE                 User confirm with the input or not need user confirm.
   2142   @retval FALSE                User want ignore this input.
   2143 
   2144 **/
   2145 BOOLEAN
   2146 FxConfirmPopup (
   2147   IN UINT32   Action
   2148   )
   2149 {
   2150   EFI_INPUT_KEY                   Key;
   2151   CHAR16                          *CfmStr;
   2152   UINTN                           CfmStrLen;
   2153   UINT32                          CheckFlags;
   2154   BOOLEAN                         RetVal;
   2155   UINTN                           CatLen;
   2156   UINTN                           MaxLen;
   2157 
   2158   CfmStrLen = 0;
   2159   CatLen    = StrLen (gConfirmMsgConnect);
   2160 
   2161   //
   2162   // Below action need extra popup dialog to confirm.
   2163   //
   2164   CheckFlags = BROWSER_ACTION_DISCARD |
   2165                BROWSER_ACTION_DEFAULT |
   2166                BROWSER_ACTION_SUBMIT |
   2167                BROWSER_ACTION_RESET |
   2168                BROWSER_ACTION_EXIT;
   2169 
   2170   //
   2171   // Not need to confirm with user, just return TRUE.
   2172   //
   2173   if ((Action & CheckFlags) == 0) {
   2174     return TRUE;
   2175   }
   2176 
   2177   if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
   2178     CfmStrLen += StrLen (gConfirmDiscardMsg);
   2179   }
   2180 
   2181   if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
   2182     if (CfmStrLen != 0) {
   2183       CfmStrLen += CatLen;
   2184     }
   2185 
   2186     CfmStrLen += StrLen (gConfirmDefaultMsg);
   2187   }
   2188 
   2189   if ((Action & BROWSER_ACTION_SUBMIT)  == BROWSER_ACTION_SUBMIT) {
   2190     if (CfmStrLen != 0) {
   2191       CfmStrLen += CatLen;
   2192     }
   2193 
   2194     CfmStrLen += StrLen (gConfirmSubmitMsg);
   2195   }
   2196 
   2197   if ((Action & BROWSER_ACTION_RESET)  == BROWSER_ACTION_RESET) {
   2198     if (CfmStrLen != 0) {
   2199       CfmStrLen += CatLen;
   2200     }
   2201 
   2202     CfmStrLen += StrLen (gConfirmResetMsg);
   2203   }
   2204 
   2205   if ((Action & BROWSER_ACTION_EXIT)  == BROWSER_ACTION_EXIT) {
   2206     if (CfmStrLen != 0) {
   2207       CfmStrLen += CatLen;
   2208     }
   2209 
   2210     CfmStrLen += StrLen (gConfirmExitMsg);
   2211   }
   2212 
   2213   //
   2214   // Allocate buffer to save the string.
   2215   // String + "?" + "\0"
   2216   //
   2217   MaxLen = CfmStrLen + 1 + 1;
   2218   CfmStr = AllocateZeroPool (MaxLen * sizeof (CHAR16));
   2219   ASSERT (CfmStr != NULL);
   2220 
   2221   if ((Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
   2222     StrCpyS (CfmStr, MaxLen, gConfirmDiscardMsg);
   2223   }
   2224 
   2225   if ((Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
   2226     if (CfmStr[0] != 0) {
   2227       StrCatS (CfmStr, MaxLen, gConfirmMsgConnect);
   2228       StrCatS (CfmStr, MaxLen, gConfirmDefaultMsg2nd);
   2229     } else {
   2230       StrCpyS (CfmStr, MaxLen, gConfirmDefaultMsg);
   2231     }
   2232   }
   2233 
   2234   if ((Action & BROWSER_ACTION_SUBMIT)  == BROWSER_ACTION_SUBMIT) {
   2235     if (CfmStr[0] != 0) {
   2236       StrCatS (CfmStr, MaxLen, gConfirmMsgConnect);
   2237       StrCatS (CfmStr, MaxLen, gConfirmSubmitMsg2nd);
   2238     } else {
   2239       StrCpyS (CfmStr, MaxLen, gConfirmSubmitMsg);
   2240     }
   2241   }
   2242 
   2243   if ((Action & BROWSER_ACTION_RESET)  == BROWSER_ACTION_RESET) {
   2244     if (CfmStr[0] != 0) {
   2245       StrCatS (CfmStr, MaxLen, gConfirmMsgConnect);
   2246       StrCatS (CfmStr, MaxLen, gConfirmResetMsg2nd);
   2247     } else {
   2248       StrCpyS (CfmStr, MaxLen, gConfirmResetMsg);
   2249     }
   2250   }
   2251 
   2252   if ((Action & BROWSER_ACTION_EXIT)  == BROWSER_ACTION_EXIT) {
   2253     if (CfmStr[0] != 0) {
   2254       StrCatS (CfmStr, MaxLen, gConfirmMsgConnect);
   2255       StrCatS (CfmStr, MaxLen, gConfirmExitMsg2nd);
   2256     } else {
   2257       StrCpyS (CfmStr, MaxLen, gConfirmExitMsg);
   2258     }
   2259   }
   2260 
   2261   StrCatS (CfmStr, MaxLen, gConfirmMsgEnd);
   2262 
   2263   do {
   2264     CreateDialog (&Key, gEmptyString, CfmStr, gConfirmOpt, gEmptyString, NULL);
   2265   } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) &&
   2266            ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (gConfirmOptNo[0] | UPPER_LOWER_CASE_OFFSET)) &&
   2267            (Key.ScanCode != SCAN_ESC));
   2268 
   2269   if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (gConfirmOptYes[0] | UPPER_LOWER_CASE_OFFSET)) {
   2270     RetVal = TRUE;
   2271   } else {
   2272     RetVal = FALSE;
   2273   }
   2274 
   2275   FreePool (CfmStr);
   2276 
   2277   return RetVal;
   2278 }
   2279 
   2280 /**
   2281   Print string for this menu option.
   2282 
   2283   @param  MenuOption               The menu opton which this attribut used to.
   2284   @param  SkipWidth                The skip width between the left to the start of the prompt.
   2285   @param  BeginCol                 The begin column for one menu.
   2286   @param  SkipLine                 The skip line for this menu.
   2287   @param  BottomRow                The bottom row for this form.
   2288   @param  Highlight                Whether this menu will be highlight.
   2289   @param  UpdateCol                Whether need to update the column info for Date/Time.
   2290 
   2291   @retval EFI_SUCESSS              Process the user selection success.
   2292 
   2293 **/
   2294 EFI_STATUS
   2295 DisplayOneMenu (
   2296   IN UI_MENU_OPTION                  *MenuOption,
   2297   IN UINTN                           SkipWidth,
   2298   IN UINTN                           BeginCol,
   2299   IN UINTN                           SkipLine,
   2300   IN UINTN                           BottomRow,
   2301   IN BOOLEAN                         Highlight,
   2302   IN BOOLEAN                         UpdateCol
   2303   )
   2304 {
   2305   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
   2306   UINTN                           Index;
   2307   UINT16                          Width;
   2308   UINT16                          PromptWidth;
   2309   CHAR16                          *StringPtr;
   2310   CHAR16                          *OptionString;
   2311   CHAR16                          *OutputString;
   2312   UINT16                          GlyphWidth;
   2313   UINTN                           Temp;
   2314   UINTN                           Temp2;
   2315   UINTN                           Temp3;
   2316   EFI_STATUS                      Status;
   2317   UINTN                           Row;
   2318   BOOLEAN                         IsProcessingFirstRow;
   2319   UINTN                           Col;
   2320   UINTN                           PromptLineNum;
   2321   UINTN                           OptionLineNum;
   2322   CHAR16                          AdjustValue;
   2323   UINTN                           MaxRow;
   2324 
   2325   Statement = MenuOption->ThisTag;
   2326   Temp      = SkipLine;
   2327   Temp2     = SkipLine;
   2328   Temp3     = SkipLine;
   2329   AdjustValue   = 0;
   2330   PromptLineNum = 0;
   2331   OptionLineNum = 0;
   2332   MaxRow        = 0;
   2333   IsProcessingFirstRow = TRUE;
   2334 
   2335   //
   2336   // Set default color.
   2337   //
   2338   SetDisplayAttribute (MenuOption, FALSE);
   2339 
   2340   //
   2341   // 1. Paint the option string.
   2342   //
   2343   Status = ProcessOptions (MenuOption, FALSE, &OptionString, FALSE);
   2344   if (EFI_ERROR (Status)) {
   2345     return Status;
   2346   }
   2347 
   2348   if (OptionString != NULL) {
   2349     if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
   2350       //
   2351       // Adjust option string for date/time opcode.
   2352       //
   2353       ProcessStringForDateTime(MenuOption, OptionString, UpdateCol);
   2354     }
   2355 
   2356     Width       = (UINT16) gOptionBlockWidth - 1;
   2357     Row         = MenuOption->Row;
   2358     GlyphWidth  = 1;
   2359     OptionLineNum = 0;
   2360 
   2361     for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
   2362       if (((Temp2 == 0)) && (Row <= BottomRow)) {
   2363         if (Statement->OpCode->OpCode == EFI_IFR_DATE_OP || Statement->OpCode->OpCode == EFI_IFR_TIME_OP) {
   2364           //
   2365           // For date/time question, it has three menu options for this qustion.
   2366           // The first/second menu options with the skip value is 0. the last one
   2367           // with skip value is 1.
   2368           //
   2369           if (MenuOption->Skip != 0) {
   2370             //
   2371             // For date/ time, print the last past (year for date and second for time)
   2372             // - 7 means skip [##/##/ for date and [##:##: for time.
   2373             //
   2374             DisplayMenuString (MenuOption,MenuOption->OptCol, Row, OutputString, Width + 1 - 7, Highlight);
   2375           } else {
   2376             //
   2377             // For date/ time, print the first and second past (year for date and second for time)
   2378             // The OutputString has a NARROW_CHAR or WIDE_CHAR at the begin of the string,
   2379             // so need to - 1 to remove it, otherwise, it will clean 1 extr char follow it.
   2380             DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, StrLen (OutputString) - 1, Highlight);
   2381           }
   2382         } else {
   2383           DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
   2384         }
   2385         OptionLineNum++;
   2386       }
   2387 
   2388       //
   2389       // If there is more string to process print on the next row and increment the Skip value
   2390       //
   2391       if (StrLen (&OptionString[Index]) != 0) {
   2392         if (Temp2 == 0) {
   2393           Row++;
   2394           //
   2395           // Since the Number of lines for this menu entry may or may not be reflected accurately
   2396           // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
   2397           // some testing to ensure we are keeping this in-sync.
   2398           //
   2399           // If the difference in rows is greater than or equal to the skip value, increase the skip value
   2400           //
   2401           if ((Row - MenuOption->Row) >= MenuOption->Skip) {
   2402             MenuOption->Skip++;
   2403           }
   2404         }
   2405       }
   2406 
   2407       FreePool (OutputString);
   2408       if (Temp2 != 0) {
   2409         Temp2--;
   2410       }
   2411     }
   2412 
   2413     Highlight = FALSE;
   2414 
   2415     FreePool (OptionString);
   2416   }
   2417 
   2418   //
   2419   // 2. Paint the description.
   2420   //
   2421   PromptWidth   = GetWidth (MenuOption, &AdjustValue);
   2422   Row           = MenuOption->Row;
   2423   GlyphWidth    = 1;
   2424   PromptLineNum = 0;
   2425 
   2426   if (MenuOption->Description == NULL || MenuOption->Description[0] == '\0') {
   2427     PrintStringAtWithWidth (BeginCol, Row, L"", PromptWidth + AdjustValue + SkipWidth);
   2428     PromptLineNum++;
   2429   } else {
   2430     for (Index = 0; GetLineByWidth (MenuOption->Description, PromptWidth, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
   2431       if ((Temp == 0) && (Row <= BottomRow)) {
   2432         //
   2433         // 1.Clean the start LEFT_SKIPPED_COLUMNS
   2434         //
   2435         PrintStringAtWithWidth (BeginCol, Row, L"", SkipWidth);
   2436 
   2437         if (Statement->OpCode->OpCode == EFI_IFR_REF_OP && MenuOption->Col >= 2 && IsProcessingFirstRow) {
   2438           //
   2439           // Print Arrow for Goto button.
   2440           //
   2441           PrintCharAt (
   2442             MenuOption->Col - 2,
   2443             Row,
   2444             GEOMETRICSHAPE_RIGHT_TRIANGLE
   2445             );
   2446           IsProcessingFirstRow = FALSE;
   2447         }
   2448         DisplayMenuString (MenuOption, MenuOption->Col, Row, OutputString, PromptWidth + AdjustValue, Highlight);
   2449         PromptLineNum ++;
   2450       }
   2451       //
   2452       // If there is more string to process print on the next row and increment the Skip value
   2453       //
   2454       if (StrLen (&MenuOption->Description[Index]) != 0) {
   2455         if (Temp == 0) {
   2456           Row++;
   2457         }
   2458       }
   2459 
   2460       FreePool (OutputString);
   2461       if (Temp != 0) {
   2462         Temp--;
   2463       }
   2464     }
   2465 
   2466     Highlight = FALSE;
   2467   }
   2468 
   2469 
   2470   //
   2471   // 3. If this is a text op with secondary text information
   2472   //
   2473   if ((Statement->OpCode->OpCode  == EFI_IFR_TEXT_OP) && (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo != 0)) {
   2474     StringPtr   = GetToken (((EFI_IFR_TEXT*)Statement->OpCode)->TextTwo, gFormData->HiiHandle);
   2475 
   2476     Width       = (UINT16) gOptionBlockWidth - 1;
   2477     Row         = MenuOption->Row;
   2478     GlyphWidth  = 1;
   2479     OptionLineNum = 0;
   2480 
   2481     for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
   2482       if ((Temp3 == 0) && (Row <= BottomRow)) {
   2483         DisplayMenuString (MenuOption, MenuOption->OptCol, Row, OutputString, Width + 1, Highlight);
   2484         OptionLineNum++;
   2485       }
   2486       //
   2487       // If there is more string to process print on the next row and increment the Skip value
   2488       //
   2489       if (StrLen (&StringPtr[Index]) != 0) {
   2490         if (Temp3 == 0) {
   2491           Row++;
   2492           //
   2493           // If the rows for text two is greater than or equal to the skip value, increase the skip value
   2494           //
   2495           if ((Row - MenuOption->Row) >= MenuOption->Skip) {
   2496             MenuOption->Skip++;
   2497           }
   2498         }
   2499       }
   2500 
   2501       FreePool (OutputString);
   2502       if (Temp3 != 0) {
   2503         Temp3--;
   2504       }
   2505     }
   2506 
   2507     FreePool (StringPtr);
   2508   }
   2509 
   2510   //
   2511   // 4.Line number for Option string and prompt string are not equal.
   2512   //  Clean the column whose line number is less.
   2513   //
   2514   if (HasOptionString(MenuOption) && (OptionLineNum != PromptLineNum)) {
   2515     Col    =  OptionLineNum < PromptLineNum ? MenuOption->OptCol : BeginCol;
   2516     Row    = (OptionLineNum < PromptLineNum ? OptionLineNum : PromptLineNum) + MenuOption->Row;
   2517     Width  = (UINT16) (OptionLineNum < PromptLineNum ? gOptionBlockWidth : PromptWidth + AdjustValue + SkipWidth);
   2518     MaxRow = (OptionLineNum < PromptLineNum ? PromptLineNum : OptionLineNum) + MenuOption->Row - 1;
   2519 
   2520     while (Row <= MaxRow) {
   2521       DisplayMenuString (MenuOption, Col, Row++, L"", Width, FALSE);
   2522     }
   2523   }
   2524 
   2525   return EFI_SUCCESS;
   2526 }
   2527 
   2528 /**
   2529   Display menu and wait for user to select one menu option, then return it.
   2530   If AutoBoot is enabled, then if user doesn't select any option,
   2531   after period of time, it will automatically return the first menu option.
   2532 
   2533   @param  FormData               The current form data info.
   2534 
   2535   @retval EFI_SUCESSS            Process the user selection success.
   2536   @retval EFI_NOT_FOUND          Process option string for orderedlist/Oneof fail.
   2537 
   2538 **/
   2539 EFI_STATUS
   2540 UiDisplayMenu (
   2541   IN  FORM_DISPLAY_ENGINE_FORM  *FormData
   2542   )
   2543 {
   2544   UINTN                           SkipValue;
   2545   INTN                            Difference;
   2546   UINTN                           DistanceValue;
   2547   UINTN                           Row;
   2548   UINTN                           Col;
   2549   UINTN                           Temp;
   2550   UINTN                           Temp2;
   2551   UINTN                           TopRow;
   2552   UINTN                           BottomRow;
   2553   UINTN                           Index;
   2554   CHAR16                          *StringPtr;
   2555   CHAR16                          *StringRightPtr;
   2556   CHAR16                          *StringErrorPtr;
   2557   CHAR16                          *OptionString;
   2558   CHAR16                          *HelpString;
   2559   CHAR16                          *HelpHeaderString;
   2560   CHAR16                          *HelpBottomString;
   2561   BOOLEAN                         NewLine;
   2562   BOOLEAN                         Repaint;
   2563   BOOLEAN                         UpArrow;
   2564   BOOLEAN                         DownArrow;
   2565   EFI_STATUS                      Status;
   2566   EFI_INPUT_KEY                   Key;
   2567   LIST_ENTRY                      *Link;
   2568   LIST_ENTRY                      *NewPos;
   2569   LIST_ENTRY                      *TopOfScreen;
   2570   LIST_ENTRY                      *SavedListEntry;
   2571   UI_MENU_OPTION                  *MenuOption;
   2572   UI_MENU_OPTION                  *NextMenuOption;
   2573   UI_MENU_OPTION                  *SavedMenuOption;
   2574   UI_CONTROL_FLAG                 ControlFlag;
   2575   UI_SCREEN_OPERATION             ScreenOperation;
   2576   FORM_DISPLAY_ENGINE_STATEMENT   *Statement;
   2577   BROWSER_HOT_KEY                 *HotKey;
   2578   UINTN                           HelpPageIndex;
   2579   UINTN                           HelpPageCount;
   2580   UINTN                           RowCount;
   2581   UINTN                           HelpLine;
   2582   UINTN                           HelpHeaderLine;
   2583   UINTN                           HelpBottomLine;
   2584   BOOLEAN                         MultiHelpPage;
   2585   UINT16                          EachLineWidth;
   2586   UINT16                          HeaderLineWidth;
   2587   UINT16                          BottomLineWidth;
   2588   EFI_STRING_ID                   HelpInfo;
   2589   UI_EVENT_TYPE                   EventType;
   2590   BOOLEAN                         SkipHighLight;
   2591   EFI_HII_VALUE                   *StatementValue;
   2592 
   2593   EventType           = UIEventNone;
   2594   Status              = EFI_SUCCESS;
   2595   HelpString          = NULL;
   2596   HelpHeaderString    = NULL;
   2597   HelpBottomString    = NULL;
   2598   OptionString        = NULL;
   2599   ScreenOperation     = UiNoOperation;
   2600   NewLine             = TRUE;
   2601   HelpPageCount       = 0;
   2602   HelpLine            = 0;
   2603   RowCount            = 0;
   2604   HelpBottomLine      = 0;
   2605   HelpHeaderLine      = 0;
   2606   HelpPageIndex       = 0;
   2607   MultiHelpPage       = FALSE;
   2608   EachLineWidth       = 0;
   2609   HeaderLineWidth     = 0;
   2610   BottomLineWidth     = 0;
   2611   UpArrow             = FALSE;
   2612   DownArrow           = FALSE;
   2613   SkipValue           = 0;
   2614   SkipHighLight       = FALSE;
   2615 
   2616   NextMenuOption      = NULL;
   2617   SavedMenuOption     = NULL;
   2618   HotKey              = NULL;
   2619   Repaint             = TRUE;
   2620   MenuOption          = NULL;
   2621   gModalSkipColumn    = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 6;
   2622 
   2623   ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
   2624 
   2625   TopRow    = gStatementDimensions.TopRow    + SCROLL_ARROW_HEIGHT;
   2626   BottomRow = gStatementDimensions.BottomRow - SCROLL_ARROW_HEIGHT - 1;
   2627 
   2628   Row = TopRow;
   2629   if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2630     Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gModalSkipColumn;
   2631   } else {
   2632     Col = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS;
   2633   }
   2634 
   2635   FindTopMenu(FormData, &TopOfScreen, &NewPos, &SkipValue);
   2636   if (!IsListEmpty (&gMenuOption)) {
   2637     NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
   2638     gUserInput->SelectedStatement = NextMenuOption->ThisTag;
   2639   }
   2640 
   2641   gST->ConOut->EnableCursor (gST->ConOut, FALSE);
   2642 
   2643   ControlFlag = CfInitialization;
   2644   while (TRUE) {
   2645     switch (ControlFlag) {
   2646     case CfInitialization:
   2647       if ((gOldFormEntry.HiiHandle != FormData->HiiHandle) ||
   2648           (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))) {
   2649         //
   2650         // Clear Statement range if different formset is painted.
   2651         //
   2652         ClearLines (
   2653           gStatementDimensions.LeftColumn,
   2654           gStatementDimensions.RightColumn,
   2655           TopRow - SCROLL_ARROW_HEIGHT,
   2656           BottomRow + SCROLL_ARROW_HEIGHT,
   2657           GetFieldTextColor ()
   2658           );
   2659 
   2660       }
   2661       ControlFlag = CfRepaint;
   2662       break;
   2663 
   2664     case CfRepaint:
   2665       ControlFlag = CfRefreshHighLight;
   2666 
   2667       if (Repaint) {
   2668         //
   2669         // Display menu
   2670         //
   2671         DownArrow       = FALSE;
   2672         UpArrow         = FALSE;
   2673         Row             = TopRow;
   2674 
   2675         gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
   2676 
   2677         //
   2678         // 1. Check whether need to print the arrow up.
   2679         //
   2680         if (!ValueIsScroll (TRUE, TopOfScreen)) {
   2681           UpArrow = TRUE;
   2682         }
   2683 
   2684         if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2685           PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
   2686         } else {
   2687           PrintStringAtWithWidth(gStatementDimensions.LeftColumn, TopRow - 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
   2688         }
   2689         if (UpArrow) {
   2690           gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
   2691           PrintCharAt (
   2692             gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
   2693             TopRow - SCROLL_ARROW_HEIGHT,
   2694             ARROW_UP
   2695             );
   2696           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
   2697         }
   2698 
   2699         //
   2700         // 2.Paint the menu.
   2701         //
   2702         for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
   2703           MenuOption          = MENU_OPTION_FROM_LINK (Link);
   2704           MenuOption->Row     = Row;
   2705           MenuOption->Col     = Col;
   2706           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2707             MenuOption->OptCol  = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth + gModalSkipColumn;
   2708           } else {
   2709             MenuOption->OptCol  = gStatementDimensions.LeftColumn + LEFT_SKIPPED_COLUMNS + gPromptBlockWidth;
   2710           }
   2711 
   2712           if (MenuOption->NestInStatement) {
   2713             MenuOption->Col += SUBTITLE_INDENT;
   2714           }
   2715 
   2716           //
   2717           // Save the highlight menu, will be used in CfRefreshHighLight case.
   2718           //
   2719           if (Link == NewPos) {
   2720             SavedMenuOption = MenuOption;
   2721             SkipHighLight   = TRUE;
   2722           }
   2723 
   2724           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2725             Status = DisplayOneMenu (MenuOption,
   2726                             MenuOption->Col - gStatementDimensions.LeftColumn,
   2727                             gStatementDimensions.LeftColumn + gModalSkipColumn,
   2728                             Link == TopOfScreen ? SkipValue : 0,
   2729                             BottomRow,
   2730                             (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
   2731                             TRUE
   2732                             );
   2733           } else {
   2734             Status = DisplayOneMenu (MenuOption,
   2735                             MenuOption->Col - gStatementDimensions.LeftColumn,
   2736                             gStatementDimensions.LeftColumn,
   2737                             Link == TopOfScreen ? SkipValue : 0,
   2738                             BottomRow,
   2739                             (BOOLEAN) ((Link == NewPos) && IsSelectable(MenuOption)),
   2740                             TRUE
   2741                             );
   2742           }
   2743 
   2744           if (EFI_ERROR (Status)) {
   2745             if (gMisMatch) {
   2746               return EFI_SUCCESS;
   2747             } else {
   2748               return Status;
   2749             }
   2750           }
   2751           //
   2752           // 3. Update the row info which will be used by next menu.
   2753           //
   2754           if (Link == TopOfScreen) {
   2755             Row += MenuOption->Skip - SkipValue;
   2756           } else {
   2757             Row += MenuOption->Skip;
   2758           }
   2759 
   2760           if (Row > BottomRow) {
   2761             if (!ValueIsScroll (FALSE, Link)) {
   2762               DownArrow = TRUE;
   2763             }
   2764 
   2765             Row = BottomRow + 1;
   2766             break;
   2767           }
   2768         }
   2769 
   2770         //
   2771         // 3. Menus in this form may not cover all form, clean the remain field.
   2772         //
   2773         while (Row <= BottomRow) {
   2774           if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2775             PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, Row++, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * gModalSkipColumn);
   2776           } else {
   2777             PrintStringAtWithWidth(gStatementDimensions.LeftColumn, Row++, L"", gStatementDimensions.RightColumn - gHelpBlockWidth - gStatementDimensions.LeftColumn);
   2778           }
   2779         }
   2780 
   2781         //
   2782         // 4. Print the down arrow row.
   2783         //
   2784         if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2785           PrintStringAtWithWidth(gStatementDimensions.LeftColumn + gModalSkipColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 *  + gModalSkipColumn);
   2786         } else {
   2787           PrintStringAtWithWidth(gStatementDimensions.LeftColumn, BottomRow + 1, L"", gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn);
   2788         }
   2789         if (DownArrow) {
   2790           gST->ConOut->SetAttribute (gST->ConOut, GetArrowColor ());
   2791           PrintCharAt (
   2792             gStatementDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
   2793             BottomRow + SCROLL_ARROW_HEIGHT,
   2794             ARROW_DOWN
   2795             );
   2796           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
   2797         }
   2798 
   2799         MenuOption = NULL;
   2800       }
   2801       break;
   2802 
   2803     case CfRefreshHighLight:
   2804 
   2805       //
   2806       // MenuOption: Last menu option that need to remove hilight
   2807       //             MenuOption is set to NULL in Repaint
   2808       // NewPos:     Current menu option that need to hilight
   2809       //
   2810       ControlFlag = CfUpdateHelpString;
   2811 
   2812       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   2813 
   2814       if (SkipHighLight) {
   2815         SkipHighLight = FALSE;
   2816         MenuOption    = SavedMenuOption;
   2817         RefreshKeyHelp(gFormData, SavedMenuOption->ThisTag, FALSE);
   2818         break;
   2819       }
   2820 
   2821       if (IsListEmpty (&gMenuOption)) {
   2822         //
   2823         // No menu option, just update the hotkey filed.
   2824         //
   2825         RefreshKeyHelp(gFormData, NULL, FALSE);
   2826         break;
   2827       }
   2828 
   2829       if (MenuOption != NULL && TopOfScreen == &MenuOption->Link) {
   2830         Temp = SkipValue;
   2831       } else {
   2832         Temp = 0;
   2833       }
   2834       if (NewPos == TopOfScreen) {
   2835         Temp2 = SkipValue;
   2836       } else {
   2837         Temp2 = 0;
   2838       }
   2839 
   2840       if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
   2841         if (MenuOption != NULL) {
   2842           //
   2843           // Remove the old highlight menu.
   2844           //
   2845           Status = DisplayOneMenu (MenuOption,
   2846                           MenuOption->Col - gStatementDimensions.LeftColumn,
   2847                           gStatementDimensions.LeftColumn,
   2848                           Temp,
   2849                           BottomRow,
   2850                           FALSE,
   2851                           FALSE
   2852                           );
   2853         }
   2854 
   2855         //
   2856         // This is the current selected statement
   2857         //
   2858         MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   2859         RefreshKeyHelp(gFormData, MenuOption->ThisTag, FALSE);
   2860 
   2861         if (!IsSelectable (MenuOption)) {
   2862           break;
   2863         }
   2864 
   2865         Status = DisplayOneMenu (MenuOption,
   2866                         MenuOption->Col - gStatementDimensions.LeftColumn,
   2867                         gStatementDimensions.LeftColumn,
   2868                         Temp2,
   2869                         BottomRow,
   2870                         TRUE,
   2871                         FALSE
   2872                         );
   2873       }
   2874       break;
   2875 
   2876     case CfUpdateHelpString:
   2877       ControlFlag = CfPrepareToReadKey;
   2878       if ((FormData->Attribute & HII_DISPLAY_MODAL) != 0) {
   2879         break;
   2880       }
   2881 
   2882       //
   2883       // NewLine means only update highlight menu (remove old highlight and highlith
   2884       // the new one), not need to full repain the form.
   2885       //
   2886       if (Repaint || NewLine) {
   2887         if (IsListEmpty (&gMenuOption)) {
   2888           //
   2889           // Don't print anything if no mwnu option.
   2890           //
   2891           StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
   2892         } else {
   2893           //
   2894           // Don't print anything if it is a NULL help token
   2895           //
   2896           ASSERT(MenuOption != NULL);
   2897           HelpInfo = ((EFI_IFR_STATEMENT_HEADER *) ((CHAR8 *)MenuOption->ThisTag->OpCode + sizeof (EFI_IFR_OP_HEADER)))->Help;
   2898           Statement = MenuOption->ThisTag;
   2899           StatementValue = &Statement->CurrentValue;
   2900           if (HelpInfo == 0 || !IsSelectable (MenuOption)) {
   2901             if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP && StatementValue->Value.date.Month== 0xff)||(Statement->OpCode->OpCode == EFI_IFR_TIME_OP && StatementValue->Value.time.Hour == 0xff)){
   2902               StringPtr = GetToken (STRING_TOKEN (GET_TIME_FAIL), gHiiHandle);
   2903             } else {
   2904               StringPtr = GetToken (STRING_TOKEN (EMPTY_STRING), gHiiHandle);
   2905             }
   2906           } else {
   2907             if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP && StatementValue->Value.date.Month== 0xff)||(Statement->OpCode->OpCode == EFI_IFR_TIME_OP && StatementValue->Value.time.Hour == 0xff)){
   2908               StringRightPtr = GetToken (HelpInfo, gFormData->HiiHandle);
   2909               StringErrorPtr = GetToken (STRING_TOKEN (GET_TIME_FAIL), gHiiHandle);
   2910               StringPtr = AllocateZeroPool ((StrLen (StringRightPtr) + StrLen (StringErrorPtr)+ 1 ) * sizeof (CHAR16));
   2911               StrCpyS (StringPtr, StrLen (StringRightPtr) + StrLen (StringErrorPtr) + 1, StringRightPtr);
   2912               StrCatS (StringPtr, StrLen (StringRightPtr) + StrLen (StringErrorPtr) + 1, StringErrorPtr);
   2913               FreePool (StringRightPtr);
   2914               FreePool (StringErrorPtr);
   2915             } else {
   2916               StringPtr = GetToken (HelpInfo, gFormData->HiiHandle);
   2917             }
   2918           }
   2919         }
   2920 
   2921         RowCount      = BottomRow - TopRow + 1;
   2922         HelpPageIndex = 0;
   2923         //
   2924         // 1.Calculate how many line the help string need to print.
   2925         //
   2926         if (HelpString != NULL) {
   2927           FreePool (HelpString);
   2928           HelpString = NULL;
   2929         }
   2930         HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount);
   2931         FreePool (StringPtr);
   2932 
   2933         if (HelpLine > RowCount) {
   2934           MultiHelpPage   = TRUE;
   2935           StringPtr       = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle);
   2936           if (HelpHeaderString != NULL) {
   2937             FreePool (HelpHeaderString);
   2938             HelpHeaderString = NULL;
   2939           }
   2940           HelpHeaderLine  = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, 0);
   2941           FreePool (StringPtr);
   2942           StringPtr       = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle);
   2943           if (HelpBottomString != NULL) {
   2944             FreePool (HelpBottomString);
   2945             HelpBottomString = NULL;
   2946           }
   2947           HelpBottomLine  = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, 0);
   2948           FreePool (StringPtr);
   2949           //
   2950           // Calculate the help page count.
   2951           //
   2952           if (HelpLine > 2 * RowCount - 2) {
   2953             HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1;
   2954             if ((HelpLine - RowCount + 1) % (RowCount - 2) != 0) {
   2955               HelpPageCount += 1;
   2956             }
   2957           } else {
   2958             HelpPageCount = 2;
   2959           }
   2960         } else {
   2961           MultiHelpPage = FALSE;
   2962         }
   2963       }
   2964 
   2965       //
   2966       // Check whether need to show the 'More(U/u)' at the begin.
   2967       // Base on current direct info, here shows aligned to the right side of the column.
   2968       // If the direction is multi line and aligned to right side may have problem, so
   2969       // add ASSERT code here.
   2970       //
   2971       if (HelpPageIndex > 0) {
   2972         gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
   2973         for (Index = 0; Index < HelpHeaderLine; Index++) {
   2974           ASSERT (HelpHeaderLine == 1);
   2975           ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1));
   2976           PrintStringAtWithWidth (
   2977             gStatementDimensions.RightColumn - gHelpBlockWidth,
   2978             Index + TopRow,
   2979             gEmptyString,
   2980             gHelpBlockWidth
   2981             );
   2982           PrintStringAt (
   2983             gStatementDimensions.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1,
   2984             Index + TopRow,
   2985             &HelpHeaderString[Index * HeaderLineWidth]
   2986             );
   2987         }
   2988       }
   2989 
   2990       gST->ConOut->SetAttribute (gST->ConOut, GetHelpTextColor ());
   2991       //
   2992       // Print the help string info.
   2993       //
   2994       if (!MultiHelpPage) {
   2995         for (Index = 0; Index < HelpLine; Index++) {
   2996           PrintStringAtWithWidth (
   2997             gStatementDimensions.RightColumn - gHelpBlockWidth,
   2998             Index + TopRow,
   2999             &HelpString[Index * EachLineWidth],
   3000             gHelpBlockWidth
   3001             );
   3002         }
   3003         for (; Index < RowCount; Index ++) {
   3004           PrintStringAtWithWidth (
   3005             gStatementDimensions.RightColumn - gHelpBlockWidth,
   3006             Index + TopRow,
   3007             gEmptyString,
   3008             gHelpBlockWidth
   3009             );
   3010         }
   3011         gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
   3012       } else  {
   3013         if (HelpPageIndex == 0) {
   3014           for (Index = 0; Index < RowCount - HelpBottomLine; Index++) {
   3015             PrintStringAtWithWidth (
   3016               gStatementDimensions.RightColumn - gHelpBlockWidth,
   3017               Index + TopRow,
   3018               &HelpString[Index * EachLineWidth],
   3019               gHelpBlockWidth
   3020               );
   3021           }
   3022         } else {
   3023           for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) &&
   3024               (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) {
   3025             PrintStringAtWithWidth (
   3026               gStatementDimensions.RightColumn - gHelpBlockWidth,
   3027               Index + TopRow + HelpHeaderLine,
   3028               &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth],
   3029               gHelpBlockWidth
   3030               );
   3031           }
   3032           if (HelpPageIndex == HelpPageCount - 1) {
   3033             for (; Index < RowCount - HelpHeaderLine; Index ++) {
   3034               PrintStringAtWithWidth (
   3035                 gStatementDimensions.RightColumn - gHelpBlockWidth,
   3036                 Index + TopRow + HelpHeaderLine,
   3037                 gEmptyString,
   3038                 gHelpBlockWidth
   3039                 );
   3040             }
   3041             gST->ConOut->SetCursorPosition(gST->ConOut, gStatementDimensions.RightColumn-1, BottomRow);
   3042           }
   3043         }
   3044       }
   3045 
   3046       //
   3047       // Check whether need to print the 'More(D/d)' at the bottom.
   3048       // Base on current direct info, here shows aligned to the right side of the column.
   3049       // If the direction is multi line and aligned to right side may have problem, so
   3050       // add ASSERT code here.
   3051       //
   3052       if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) {
   3053         gST->ConOut->SetAttribute (gST->ConOut, GetInfoTextColor ());
   3054         for (Index = 0; Index < HelpBottomLine; Index++) {
   3055           ASSERT (HelpBottomLine == 1);
   3056           ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1));
   3057           PrintStringAtWithWidth (
   3058             gStatementDimensions.RightColumn - gHelpBlockWidth,
   3059             BottomRow + Index - HelpBottomLine + 1,
   3060             gEmptyString,
   3061             gHelpBlockWidth
   3062             );
   3063           PrintStringAt (
   3064             gStatementDimensions.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1,
   3065             BottomRow + Index - HelpBottomLine + 1,
   3066             &HelpBottomString[Index * BottomLineWidth]
   3067             );
   3068         }
   3069       }
   3070       //
   3071       // Reset this flag every time we finish using it.
   3072       //
   3073       Repaint = FALSE;
   3074       NewLine = FALSE;
   3075       break;
   3076 
   3077     case CfPrepareToReadKey:
   3078       ControlFlag = CfReadKey;
   3079       ScreenOperation = UiNoOperation;
   3080       break;
   3081 
   3082     case CfReadKey:
   3083       ControlFlag = CfScreenOperation;
   3084 
   3085       //
   3086       // Wait for user's selection
   3087       //
   3088       while (TRUE) {
   3089         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3090         if (!EFI_ERROR (Status)) {
   3091           EventType = UIEventKey;
   3092           break;
   3093         }
   3094 
   3095         //
   3096         // If we encounter error, continue to read another key in.
   3097         //
   3098         if (Status != EFI_NOT_READY) {
   3099           continue;
   3100         }
   3101 
   3102         EventType = UiWaitForEvent(gST->ConIn->WaitForKey);
   3103         if (EventType == UIEventKey) {
   3104           gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3105         }
   3106         break;
   3107       }
   3108 
   3109       if (EventType == UIEventDriver) {
   3110         gMisMatch = TRUE;
   3111         gUserInput->Action = BROWSER_ACTION_NONE;
   3112         ControlFlag = CfExit;
   3113         break;
   3114       }
   3115 
   3116       if (EventType == UIEventTimeOut) {
   3117         gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
   3118         ControlFlag = CfExit;
   3119         break;
   3120       }
   3121 
   3122       switch (Key.UnicodeChar) {
   3123       case CHAR_CARRIAGE_RETURN:
   3124         if(MenuOption == NULL || MenuOption->GrayOut || MenuOption->ReadOnly) {
   3125           ControlFlag = CfReadKey;
   3126           break;
   3127         }
   3128 
   3129         ScreenOperation = UiSelect;
   3130         gDirection      = 0;
   3131         break;
   3132 
   3133       //
   3134       // We will push the adjustment of these numeric values directly to the input handler
   3135       //  NOTE: we won't handle manual input numeric
   3136       //
   3137       case '+':
   3138       case '-':
   3139         //
   3140         // If the screen has no menu items, and the user didn't select UiReset
   3141         // ignore the selection and go back to reading keys.
   3142         //
   3143         ASSERT(MenuOption != NULL);
   3144         if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
   3145           ControlFlag = CfReadKey;
   3146           break;
   3147         }
   3148 
   3149         Statement = MenuOption->ThisTag;
   3150         if ((Statement->OpCode->OpCode == EFI_IFR_DATE_OP)
   3151           || (Statement->OpCode->OpCode == EFI_IFR_TIME_OP)
   3152           || ((Statement->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (GetFieldFromNum(Statement->OpCode) != 0))
   3153         ){
   3154           if (Key.UnicodeChar == '+') {
   3155             gDirection = SCAN_RIGHT;
   3156           } else {
   3157             gDirection = SCAN_LEFT;
   3158           }
   3159 
   3160           Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
   3161           if (OptionString != NULL) {
   3162             FreePool (OptionString);
   3163           }
   3164           if (EFI_ERROR (Status)) {
   3165             //
   3166             // Repaint to clear possible error prompt pop-up
   3167             //
   3168             Repaint = TRUE;
   3169             NewLine = TRUE;
   3170           } else {
   3171             ControlFlag = CfExit;
   3172           }
   3173         }
   3174         break;
   3175 
   3176       case '^':
   3177         ScreenOperation = UiUp;
   3178         break;
   3179 
   3180       case 'V':
   3181       case 'v':
   3182         ScreenOperation = UiDown;
   3183         break;
   3184 
   3185       case ' ':
   3186         if(IsListEmpty (&gMenuOption)) {
   3187           ControlFlag = CfReadKey;
   3188           break;
   3189         }
   3190 
   3191         ASSERT(MenuOption != NULL);
   3192         if (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
   3193           ScreenOperation = UiSelect;
   3194         }
   3195         break;
   3196 
   3197       case 'D':
   3198       case 'd':
   3199         if (!MultiHelpPage) {
   3200           ControlFlag = CfReadKey;
   3201           break;
   3202         }
   3203         ControlFlag    = CfUpdateHelpString;
   3204         HelpPageIndex  = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1;
   3205         break;
   3206 
   3207       case 'U':
   3208       case 'u':
   3209         if (!MultiHelpPage) {
   3210           ControlFlag = CfReadKey;
   3211           break;
   3212         }
   3213         ControlFlag    = CfUpdateHelpString;
   3214         HelpPageIndex  = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0;
   3215         break;
   3216 
   3217       case CHAR_NULL:
   3218         for (Index = 0; Index < mScanCodeNumber; Index++) {
   3219           if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
   3220             ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
   3221             break;
   3222           }
   3223         }
   3224 
   3225         if (((FormData->Attribute & HII_DISPLAY_MODAL) != 0) && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) {
   3226           //
   3227           // ModalForm has no ESC key and Hot Key.
   3228           //
   3229           ControlFlag = CfReadKey;
   3230         } else if (Index == mScanCodeNumber) {
   3231           //
   3232           // Check whether Key matches the registered hot key.
   3233           //
   3234           HotKey = NULL;
   3235           HotKey = GetHotKeyFromRegisterList (&Key);
   3236           if (HotKey != NULL) {
   3237             ScreenOperation = UiHotKey;
   3238           }
   3239         }
   3240         break;
   3241       }
   3242       break;
   3243 
   3244     case CfScreenOperation:
   3245       if ((ScreenOperation != UiReset) && (ScreenOperation != UiHotKey)) {
   3246         //
   3247         // If the screen has no menu items, and the user didn't select UiReset or UiHotKey
   3248         // ignore the selection and go back to reading keys.
   3249         //
   3250         if (IsListEmpty (&gMenuOption)) {
   3251           ControlFlag = CfReadKey;
   3252           break;
   3253         }
   3254       }
   3255 
   3256       for (Index = 0;
   3257            Index < ARRAY_SIZE (gScreenOperationToControlFlag);
   3258            Index++
   3259           ) {
   3260         if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
   3261           ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
   3262           break;
   3263         }
   3264       }
   3265       break;
   3266 
   3267     case CfUiSelect:
   3268       ControlFlag = CfRepaint;
   3269 
   3270       ASSERT(MenuOption != NULL);
   3271       Statement = MenuOption->ThisTag;
   3272       if (Statement->OpCode->OpCode == EFI_IFR_TEXT_OP) {
   3273         break;
   3274       }
   3275 
   3276       switch (Statement->OpCode->OpCode) {
   3277       case EFI_IFR_REF_OP:
   3278       case EFI_IFR_ACTION_OP:
   3279       case EFI_IFR_RESET_BUTTON_OP:
   3280         ControlFlag = CfExit;
   3281         break;
   3282 
   3283       default:
   3284         //
   3285         // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
   3286         //
   3287         RefreshKeyHelp (gFormData, Statement, TRUE);
   3288         Status = ProcessOptions (MenuOption, TRUE, &OptionString, TRUE);
   3289 
   3290         if (OptionString != NULL) {
   3291           FreePool (OptionString);
   3292         }
   3293 
   3294         if (EFI_ERROR (Status)) {
   3295           Repaint = TRUE;
   3296           NewLine = TRUE;
   3297           RefreshKeyHelp (gFormData, Statement, FALSE);
   3298           break;
   3299         } else {
   3300           ControlFlag = CfExit;
   3301           break;
   3302         }
   3303       }
   3304       break;
   3305 
   3306     case CfUiReset:
   3307       //
   3308       // We come here when someone press ESC
   3309       // If the policy is not exit front page when user press ESC, process here.
   3310       //
   3311       if (!FormExitPolicy()) {
   3312         Repaint     = TRUE;
   3313         NewLine     = TRUE;
   3314         ControlFlag = CfRepaint;
   3315         break;
   3316       }
   3317 
   3318       gUserInput->Action = BROWSER_ACTION_FORM_EXIT;
   3319       ControlFlag = CfExit;
   3320       break;
   3321 
   3322     case CfUiHotKey:
   3323       ControlFlag = CfRepaint;
   3324 
   3325       ASSERT (HotKey != NULL);
   3326 
   3327       if (FxConfirmPopup(HotKey->Action)) {
   3328         gUserInput->Action = HotKey->Action;
   3329         if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
   3330           gUserInput->DefaultId = HotKey->DefaultId;
   3331         }
   3332         ControlFlag = CfExit;
   3333       } else {
   3334         Repaint     = TRUE;
   3335         NewLine     = TRUE;
   3336         ControlFlag = CfRepaint;
   3337       }
   3338 
   3339       break;
   3340 
   3341     case CfUiLeft:
   3342       ControlFlag = CfRepaint;
   3343       ASSERT(MenuOption != NULL);
   3344       if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
   3345         if (MenuOption->Sequence != 0) {
   3346           //
   3347           // In the middle or tail of the Date/Time op-code set, go left.
   3348           //
   3349           ASSERT(NewPos != NULL);
   3350           NewPos = NewPos->BackLink;
   3351         }
   3352       }
   3353       break;
   3354 
   3355     case CfUiRight:
   3356       ControlFlag = CfRepaint;
   3357       ASSERT(MenuOption != NULL);
   3358       if ((MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->OpCode->OpCode == EFI_IFR_TIME_OP)) {
   3359         if (MenuOption->Sequence != 2) {
   3360           //
   3361           // In the middle or tail of the Date/Time op-code set, go left.
   3362           //
   3363           ASSERT(NewPos != NULL);
   3364           NewPos = NewPos->ForwardLink;
   3365         }
   3366       }
   3367       break;
   3368 
   3369     case CfUiUp:
   3370       ControlFlag = CfRepaint;
   3371       NewLine     = TRUE;
   3372 
   3373       SavedListEntry = NewPos;
   3374       ASSERT(NewPos != NULL);
   3375 
   3376       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   3377       ASSERT (MenuOption != NULL);
   3378 
   3379       //
   3380       // Adjust Date/Time position before we advance forward.
   3381       //
   3382       AdjustDateAndTimePosition (TRUE, &NewPos);
   3383 
   3384       NewPos     = NewPos->BackLink;
   3385       //
   3386       // Find next selectable menu or the first menu beyond current form.
   3387       //
   3388       Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow, FALSE);
   3389       if (Difference < 0) {
   3390         //
   3391         // We hit the begining MenuOption that can be focused
   3392         // so we simply scroll to the top.
   3393         //
   3394         Repaint     = TRUE;
   3395         if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
   3396           TopOfScreen = gMenuOption.ForwardLink;
   3397           NewPos      = SavedListEntry;
   3398           SkipValue = 0;
   3399         } else {
   3400           //
   3401           // Scroll up to the last page when we have arrived at top page.
   3402           //
   3403           TopOfScreen = FindTopOfScreenMenu (gMenuOption.BackLink, BottomRow - TopRow, &SkipValue);
   3404           NewPos = gMenuOption.BackLink;
   3405           MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow, TRUE);
   3406         }
   3407       } else {
   3408         NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
   3409 
   3410         if (MenuOption->Row < TopRow + Difference + NextMenuOption->Skip) {
   3411           //
   3412           // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
   3413           //
   3414           TopOfScreen = NewPos;
   3415           Repaint     = TRUE;
   3416           SkipValue   = 0;
   3417         }
   3418 
   3419         //
   3420         // Check whether new highlight menu is selectable, if not, keep highlight on the old one.
   3421         //
   3422         // BottomRow - TopRow + 1 means the total rows current forms supported.
   3423         // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
   3424         // and new top menu. New top menu will all shows in next form, but last highlight menu
   3425         // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
   3426         // last highlight menu.
   3427         //
   3428         if (!IsSelectable(NextMenuOption) && IsSelectable(MenuOption) &&
   3429             (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
   3430           NewPos = SavedListEntry;
   3431         }
   3432       }
   3433 
   3434       UpdateStatusBar (INPUT_ERROR, FALSE);
   3435 
   3436       //
   3437       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
   3438       //
   3439       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
   3440       AdjustDateAndTimePosition (TRUE, &NewPos);
   3441 
   3442       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   3443       break;
   3444 
   3445     case CfUiPageUp:
   3446       //
   3447       // SkipValue means lines is skipped when show the top menu option.
   3448       //
   3449       ControlFlag = CfRepaint;
   3450       NewLine     = TRUE;
   3451       Repaint     = TRUE;
   3452 
   3453       Link      = TopOfScreen;
   3454       //
   3455       // First minus the menu of the top screen, it's value is SkipValue.
   3456       //
   3457       if (SkipValue >= BottomRow - TopRow + 1) {
   3458         //
   3459         // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
   3460         // form of options to be show, so just update the SkipValue to show the next
   3461         // parts of options.
   3462         //
   3463         SkipValue -= BottomRow - TopRow + 1;
   3464         NewPos     = TopOfScreen;
   3465         break;
   3466       } else {
   3467         Index     = (BottomRow + 1) - SkipValue - TopRow;
   3468       }
   3469 
   3470       TopOfScreen = FindTopOfScreenMenu(TopOfScreen, Index, &SkipValue);
   3471       NewPos = TopOfScreen;
   3472       MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, FALSE);
   3473 
   3474       UpdateStatusBar (INPUT_ERROR, FALSE);
   3475 
   3476       //
   3477       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
   3478       // Don't do this when we are already in the first page.
   3479       //
   3480       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
   3481       AdjustDateAndTimePosition (TRUE, &NewPos);
   3482 
   3483       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   3484       break;
   3485 
   3486     case CfUiPageDown:
   3487       //
   3488       // SkipValue means lines is skipped when show the top menu option.
   3489       //
   3490       ControlFlag = CfRepaint;
   3491       NewLine     = TRUE;
   3492       Repaint     = TRUE;
   3493 
   3494       Link    = TopOfScreen;
   3495       NextMenuOption = MENU_OPTION_FROM_LINK (Link);
   3496       Index = TopRow + NextMenuOption->Skip - SkipValue;
   3497       //
   3498       // Count to the menu option which will show at the top of the next form.
   3499       //
   3500       while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) {
   3501         Link           = Link->ForwardLink;
   3502         NextMenuOption = MENU_OPTION_FROM_LINK (Link);
   3503         Index = Index + NextMenuOption->Skip;
   3504       }
   3505 
   3506       if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) {
   3507         //
   3508         // Highlight on the last menu which can be highlight.
   3509         //
   3510         Repaint = FALSE;
   3511         MoveToNextStatement (TRUE, &Link, Index - TopRow, TRUE);
   3512       } else {
   3513         //
   3514         // Calculate the skip line for top of screen menu.
   3515         //
   3516         if (Link == TopOfScreen) {
   3517           //
   3518           // The top of screen menu option occupies the entire form.
   3519           //
   3520           SkipValue += BottomRow - TopRow + 1;
   3521         } else {
   3522           SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1));
   3523         }
   3524         TopOfScreen = Link;
   3525         MenuOption = NULL;
   3526         //
   3527         // Move to the Next selectable menu.
   3528         //
   3529         MoveToNextStatement (FALSE, &Link, BottomRow - TopRow, TRUE);
   3530       }
   3531 
   3532       //
   3533       // Save the menu as the next highlight menu.
   3534       //
   3535       NewPos  = Link;
   3536 
   3537       UpdateStatusBar (INPUT_ERROR, FALSE);
   3538 
   3539       //
   3540       // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
   3541       // Don't do this when we are already in the last page.
   3542       //
   3543       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
   3544       AdjustDateAndTimePosition (TRUE, &NewPos);
   3545 
   3546       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   3547       break;
   3548 
   3549     case CfUiDown:
   3550       //
   3551       // SkipValue means lines is skipped when show the top menu option.
   3552       // NewPos  points to the menu which is highlighted now.
   3553       //
   3554       ControlFlag = CfRepaint;
   3555       NewLine     = TRUE;
   3556 
   3557       if (NewPos == TopOfScreen) {
   3558         Temp2 = SkipValue;
   3559       } else {
   3560         Temp2 = 0;
   3561       }
   3562 
   3563       SavedListEntry = NewPos;
   3564       //
   3565       // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
   3566       // to be one that progresses to the next set of op-codes, we need to advance to the last
   3567       // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
   3568       // checking can be done.  The only other logic we need to introduce is that if a Date/Time
   3569       // op-code is the last entry in the menu, we need to rewind back to the first op-code of
   3570       // the Date/Time op-code.
   3571       //
   3572       AdjustDateAndTimePosition (FALSE, &NewPos);
   3573 
   3574       MenuOption = MENU_OPTION_FROM_LINK (NewPos);
   3575       NewPos     = NewPos->ForwardLink;
   3576       //
   3577       // Find the next selectable menu.
   3578       //
   3579       if (MenuOption->Row + MenuOption->Skip - Temp2 > BottomRow + 1) {
   3580         if (gMenuOption.ForwardLink == NewPos || &gMenuOption == NewPos) {
   3581           Difference = -1;
   3582         } else {
   3583           Difference = 0;
   3584         }
   3585       } else {
   3586         Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow + 1 - (MenuOption->Row + MenuOption->Skip - Temp2), FALSE);
   3587       }
   3588       if (Difference < 0) {
   3589         //
   3590         // Scroll to the first page.
   3591         //
   3592         if (TopOfScreen != gMenuOption.ForwardLink || SkipValue != 0) {
   3593           TopOfScreen = gMenuOption.ForwardLink;
   3594           Repaint     = TRUE;
   3595           MenuOption  = NULL;
   3596         } else {
   3597           MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
   3598         }
   3599         NewPos        = gMenuOption.ForwardLink;
   3600         MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow, TRUE);
   3601 
   3602         SkipValue = 0;
   3603         //
   3604         // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
   3605         //
   3606         AdjustDateAndTimePosition (TRUE, &TopOfScreen);
   3607         AdjustDateAndTimePosition (TRUE, &NewPos);
   3608 
   3609         UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   3610         break;
   3611       }
   3612 
   3613       //
   3614       // Get next selected menu info.
   3615       //
   3616       AdjustDateAndTimePosition (FALSE, &NewPos);
   3617       NextMenuOption  = MENU_OPTION_FROM_LINK (NewPos);
   3618       if (NextMenuOption->Row == 0) {
   3619         UpdateOptionSkipLines (NextMenuOption);
   3620       }
   3621 
   3622       //
   3623       // Calculate new highlight menu end row.
   3624       //
   3625       Temp = (MenuOption->Row + MenuOption->Skip - Temp2) + Difference + NextMenuOption->Skip - 1;
   3626       if (Temp > BottomRow) {
   3627         //
   3628         // Get the top screen menu info.
   3629         //
   3630         AdjustDateAndTimePosition (FALSE, &TopOfScreen);
   3631         SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
   3632 
   3633         //
   3634         // Current Top screen menu occupy (SavedMenuOption->Skip - SkipValue) rows.
   3635         // Full shows the new selected menu need to skip (Temp - BottomRow - 1) rows.
   3636         //
   3637         if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) {
   3638           //
   3639           // Skip the top op-code
   3640           //
   3641           TopOfScreen   = TopOfScreen->ForwardLink;
   3642           DistanceValue = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue);
   3643 
   3644           SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
   3645 
   3646           //
   3647           // If we have a remainder, skip that many more op-codes until we drain the remainder
   3648           // Special case is the selected highlight menu has more than one form of menus.
   3649           //
   3650           while (DistanceValue >= SavedMenuOption->Skip && TopOfScreen != NewPos) {
   3651             //
   3652             // Since the Difference is greater than or equal to this op-code's skip value, skip it
   3653             //
   3654             DistanceValue   = DistanceValue - (INTN) SavedMenuOption->Skip;
   3655             TopOfScreen     = TopOfScreen->ForwardLink;
   3656             SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
   3657           }
   3658           //
   3659           // Since we will act on this op-code in the next routine, and increment the
   3660           // SkipValue, set the skips to one less than what is required.
   3661           //
   3662           if (TopOfScreen != NewPos) {
   3663             SkipValue = DistanceValue;
   3664           } else {
   3665             SkipValue = 0;
   3666           }
   3667         } else {
   3668           //
   3669           // Since we will act on this op-code in the next routine, and increment the
   3670           // SkipValue, set the skips to one less than what is required.
   3671           //
   3672           SkipValue += Temp - BottomRow;
   3673         }
   3674         Repaint       = TRUE;
   3675       } else if (!IsSelectable (NextMenuOption)) {
   3676         //
   3677         // Continue to go down until scroll to next page or the selectable option is found.
   3678         //
   3679         ScreenOperation = UiDown;
   3680         ControlFlag     = CfScreenOperation;
   3681         break;
   3682       }
   3683 
   3684       MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
   3685 
   3686       //
   3687       // Check whether new highlight menu is selectable, if not, keep highlight on the old one.
   3688       //
   3689       // BottomRow - TopRow + 1 means the total rows current forms supported.
   3690       // Difference + NextMenuOption->Skip + 1 means the distance between last highlight menu
   3691       // and new top menu. New top menu will all shows in next form, but last highlight menu
   3692       // may only shows 1 line. + 1 at right part means at least need to keep 1 line for the
   3693       // last highlight menu.
   3694       //
   3695       if (!IsSelectable (NextMenuOption) && IsSelectable (MenuOption) &&
   3696          (BottomRow - TopRow + 1 >= Difference + NextMenuOption->Skip + 1)) {
   3697         NewPos = SavedListEntry;
   3698       }
   3699 
   3700       UpdateStatusBar (INPUT_ERROR, FALSE);
   3701 
   3702       //
   3703       // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
   3704       //
   3705       AdjustDateAndTimePosition (TRUE, &TopOfScreen);
   3706       AdjustDateAndTimePosition (TRUE, &NewPos);
   3707 
   3708       UpdateHighlightMenuInfo(NewPos, TopOfScreen, SkipValue);
   3709       break;
   3710 
   3711     case CfUiNoOperation:
   3712       ControlFlag = CfRepaint;
   3713       break;
   3714 
   3715     case CfExit:
   3716       gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
   3717       if (HelpString != NULL) {
   3718         FreePool (HelpString);
   3719       }
   3720       if (HelpHeaderString != NULL) {
   3721         FreePool (HelpHeaderString);
   3722       }
   3723       if (HelpBottomString != NULL) {
   3724         FreePool (HelpBottomString);
   3725       }
   3726       return EFI_SUCCESS;
   3727 
   3728     default:
   3729       break;
   3730     }
   3731   }
   3732 }
   3733 
   3734 /**
   3735   Free the UI Menu Option structure data.
   3736 
   3737   @param   MenuOptionList         Point to the menu option list which need to be free.
   3738 
   3739 **/
   3740 VOID
   3741 FreeMenuOptionData(
   3742   LIST_ENTRY           *MenuOptionList
   3743   )
   3744 {
   3745   LIST_ENTRY           *Link;
   3746   UI_MENU_OPTION       *Option;
   3747 
   3748   //
   3749   // Free menu option list
   3750   //
   3751   while (!IsListEmpty (MenuOptionList)) {
   3752     Link = GetFirstNode (MenuOptionList);
   3753     Option = MENU_OPTION_FROM_LINK (Link);
   3754     if (Option->Description != NULL){
   3755       FreePool(Option->Description);
   3756     }
   3757     RemoveEntryList (&Option->Link);
   3758     FreePool (Option);
   3759   }
   3760 }
   3761 
   3762 /**
   3763 
   3764   Base on the browser status info to show an pop up message.
   3765 
   3766 **/
   3767 VOID
   3768 BrowserStatusProcess (
   3769   VOID
   3770   )
   3771 {
   3772   CHAR16             *ErrorInfo;
   3773   EFI_INPUT_KEY      Key;
   3774   EFI_EVENT          WaitList[2];
   3775   EFI_EVENT          RefreshIntervalEvent;
   3776   EFI_EVENT          TimeOutEvent;
   3777   UINT8              TimeOut;
   3778   EFI_STATUS         Status;
   3779   UINTN              Index;
   3780   WARNING_IF_CONTEXT EventContext;
   3781   EFI_IFR_OP_HEADER  *OpCodeBuf;
   3782   EFI_STRING_ID      StringToken;
   3783   CHAR16             DiscardChange;
   3784   CHAR16             JumpToFormSet;
   3785   CHAR16             *PrintString;
   3786 
   3787   if (gFormData->BrowserStatus == BROWSER_SUCCESS) {
   3788     return;
   3789   }
   3790 
   3791   StringToken          = 0;
   3792   TimeOutEvent         = NULL;
   3793   RefreshIntervalEvent = NULL;
   3794   OpCodeBuf            = NULL;
   3795   if (gFormData->HighLightedStatement != NULL) {
   3796     OpCodeBuf = gFormData->HighLightedStatement->OpCode;
   3797   }
   3798 
   3799   if (gFormData->BrowserStatus == (BROWSER_WARNING_IF)) {
   3800     ASSERT (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_WARNING_IF_OP);
   3801 
   3802     TimeOut     = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->TimeOut;
   3803     StringToken = ((EFI_IFR_WARNING_IF *) OpCodeBuf)->Warning;
   3804   } else {
   3805     TimeOut = 0;
   3806     if ((gFormData->BrowserStatus == (BROWSER_NO_SUBMIT_IF)) &&
   3807         (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_NO_SUBMIT_IF_OP)) {
   3808       StringToken = ((EFI_IFR_NO_SUBMIT_IF *) OpCodeBuf)->Error;
   3809     } else if ((gFormData->BrowserStatus == (BROWSER_INCONSISTENT_IF)) &&
   3810                (OpCodeBuf != NULL && OpCodeBuf->OpCode == EFI_IFR_INCONSISTENT_IF_OP)) {
   3811       StringToken = ((EFI_IFR_INCONSISTENT_IF *) OpCodeBuf)->Error;
   3812     }
   3813   }
   3814 
   3815   if (StringToken != 0) {
   3816     ErrorInfo = GetToken (StringToken, gFormData->HiiHandle);
   3817   } else if (gFormData->ErrorString != NULL) {
   3818     //
   3819     // Only used to compatible with old setup browser.
   3820     // Not use this field in new browser core.
   3821     //
   3822     ErrorInfo = gFormData->ErrorString;
   3823   } else {
   3824     switch (gFormData->BrowserStatus) {
   3825     case BROWSER_SUBMIT_FAIL:
   3826       ErrorInfo = gSaveFailed;
   3827       break;
   3828 
   3829     case BROWSER_FORM_NOT_FOUND:
   3830       ErrorInfo = gFormNotFound;
   3831       break;
   3832 
   3833     case BROWSER_FORM_SUPPRESS:
   3834       ErrorInfo = gFormSuppress;
   3835       break;
   3836 
   3837     case BROWSER_PROTOCOL_NOT_FOUND:
   3838       ErrorInfo = gProtocolNotFound;
   3839       break;
   3840 
   3841     case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
   3842       ErrorInfo = gNoSubmitIfFailed;
   3843       break;
   3844 
   3845     case BROWSER_RECONNECT_FAIL:
   3846       ErrorInfo = gReconnectFail;
   3847       break;
   3848 
   3849     case BROWSER_RECONNECT_SAVE_CHANGES:
   3850       ErrorInfo = gReconnectConfirmChanges;
   3851       break;
   3852 
   3853     case BROWSER_RECONNECT_REQUIRED:
   3854       ErrorInfo = gReconnectRequired;
   3855       break;
   3856 
   3857     default:
   3858       ErrorInfo = gBrowserError;
   3859       break;
   3860     }
   3861   }
   3862 
   3863   switch (gFormData->BrowserStatus) {
   3864   case BROWSER_SUBMIT_FAIL:
   3865   case BROWSER_SUBMIT_FAIL_NO_SUBMIT_IF:
   3866   case BROWSER_RECONNECT_SAVE_CHANGES:
   3867     ASSERT (gUserInput != NULL);
   3868     if (gFormData->BrowserStatus == (BROWSER_SUBMIT_FAIL)) {
   3869       PrintString = gSaveProcess;
   3870       JumpToFormSet = gJumpToFormSet[0];
   3871       DiscardChange = gDiscardChange[0];
   3872     } else if (gFormData->BrowserStatus == (BROWSER_RECONNECT_SAVE_CHANGES)){
   3873       PrintString = gChangesOpt;
   3874       JumpToFormSet = gConfirmOptYes[0];
   3875       DiscardChange = gConfirmOptNo[0];
   3876     } else {
   3877       PrintString = gSaveNoSubmitProcess;
   3878       JumpToFormSet = gCheckError[0];
   3879       DiscardChange = gDiscardChange[0];
   3880     }
   3881 
   3882     do {
   3883       CreateDialog (&Key, gEmptyString, ErrorInfo, PrintString, gEmptyString, NULL);
   3884     } while (((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (DiscardChange | UPPER_LOWER_CASE_OFFSET)) &&
   3885              ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (JumpToFormSet | UPPER_LOWER_CASE_OFFSET)));
   3886 
   3887     if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (DiscardChange | UPPER_LOWER_CASE_OFFSET)) {
   3888       gUserInput->Action = BROWSER_ACTION_DISCARD;
   3889     } else {
   3890       gUserInput->Action = BROWSER_ACTION_GOTO;
   3891     }
   3892     break;
   3893 
   3894   default:
   3895     if (TimeOut == 0) {
   3896       do {
   3897         CreateDialog (&Key, gEmptyString, ErrorInfo, gPressEnter, gEmptyString, NULL);
   3898       } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
   3899     } else {
   3900       Status = gBS->CreateEvent (EVT_NOTIFY_WAIT, TPL_CALLBACK, EmptyEventProcess, NULL, &TimeOutEvent);
   3901       ASSERT_EFI_ERROR (Status);
   3902 
   3903       EventContext.SyncEvent = TimeOutEvent;
   3904       EventContext.TimeOut   = &TimeOut;
   3905       EventContext.ErrorInfo = ErrorInfo;
   3906 
   3907       Status = gBS->CreateEvent (EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_CALLBACK, RefreshTimeOutProcess, &EventContext, &RefreshIntervalEvent);
   3908       ASSERT_EFI_ERROR (Status);
   3909 
   3910       //
   3911       // Show the dialog first to avoid long time not reaction.
   3912       //
   3913       gBS->SignalEvent (RefreshIntervalEvent);
   3914 
   3915       Status = gBS->SetTimer (RefreshIntervalEvent, TimerPeriodic, ONE_SECOND);
   3916       ASSERT_EFI_ERROR (Status);
   3917 
   3918       while (TRUE) {
   3919         Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
   3920         if (!EFI_ERROR (Status) && Key.UnicodeChar == CHAR_CARRIAGE_RETURN) {
   3921           break;
   3922         }
   3923 
   3924         if (Status != EFI_NOT_READY) {
   3925           continue;
   3926         }
   3927 
   3928         WaitList[0] = TimeOutEvent;
   3929         WaitList[1] = gST->ConIn->WaitForKey;
   3930 
   3931         Status = gBS->WaitForEvent (2, WaitList, &Index);
   3932         ASSERT_EFI_ERROR (Status);
   3933 
   3934         if (Index == 0) {
   3935           //
   3936           // Timeout occur, close the hoot time out event.
   3937           //
   3938           break;
   3939         }
   3940       }
   3941 
   3942       gBS->CloseEvent (TimeOutEvent);
   3943       gBS->CloseEvent (RefreshIntervalEvent);
   3944     }
   3945     break;
   3946   }
   3947 
   3948   if (StringToken != 0) {
   3949     FreePool (ErrorInfo);
   3950   }
   3951 }
   3952 
   3953 /**
   3954   Display one form, and return user input.
   3955 
   3956   @param FormData                Form Data to be shown.
   3957   @param UserInputData           User input data.
   3958 
   3959   @retval EFI_SUCCESS            1.Form Data is shown, and user input is got.
   3960                                  2.Error info has show and return.
   3961   @retval EFI_INVALID_PARAMETER  The input screen dimension is not valid
   3962   @retval EFI_NOT_FOUND          New form data has some error.
   3963 **/
   3964 EFI_STATUS
   3965 EFIAPI
   3966 FormDisplay (
   3967   IN  FORM_DISPLAY_ENGINE_FORM  *FormData,
   3968   OUT USER_INPUT                *UserInputData
   3969   )
   3970 {
   3971   EFI_STATUS  Status;
   3972 
   3973   ASSERT (FormData != NULL);
   3974   if (FormData == NULL) {
   3975     return EFI_INVALID_PARAMETER;
   3976   }
   3977 
   3978   gUserInput = UserInputData;
   3979   gFormData  = FormData;
   3980 
   3981   //
   3982   // Process the status info first.
   3983   //
   3984   BrowserStatusProcess();
   3985   if (gFormData->BrowserStatus != BROWSER_SUCCESS) {
   3986     //
   3987     // gFormData->BrowserStatus != BROWSER_SUCCESS, means only need to print the error info, return here.
   3988     //
   3989     return EFI_SUCCESS;
   3990   }
   3991 
   3992   Status = DisplayPageFrame (FormData, &gStatementDimensions);
   3993   if (EFI_ERROR (Status)) {
   3994     return Status;
   3995   }
   3996 
   3997   //
   3998   // Global Widths should be initialized before any MenuOption creation
   3999   // or the GetWidth() used in UiAddMenuOption() will return incorrect value.
   4000   //
   4001   //
   4002   //  Left                                              right
   4003   //   |<-.->|<-.........->|<- .........->|<-...........->|
   4004   //     Skip    Prompt         Option         Help
   4005   //
   4006   gOptionBlockWidth = (CHAR16) ((gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn) / 3) + 1;
   4007   gHelpBlockWidth   = (CHAR16) (gOptionBlockWidth - 1 - LEFT_SKIPPED_COLUMNS);
   4008   gPromptBlockWidth = (CHAR16) (gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn - 2 * (gOptionBlockWidth - 1) - 1);
   4009 
   4010   ConvertStatementToMenu();
   4011 
   4012   //
   4013   // Check whether layout is changed.
   4014   //
   4015   if (mIsFirstForm
   4016       || (gOldFormEntry.HiiHandle != FormData->HiiHandle)
   4017       || (!CompareGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid))
   4018       || (gOldFormEntry.FormId != FormData->FormId)) {
   4019     mStatementLayoutIsChanged = TRUE;
   4020   } else {
   4021     mStatementLayoutIsChanged = FALSE;
   4022   }
   4023 
   4024   Status = UiDisplayMenu(FormData);
   4025 
   4026   //
   4027   // Backup last form info.
   4028   //
   4029   mIsFirstForm            = FALSE;
   4030   gOldFormEntry.HiiHandle = FormData->HiiHandle;
   4031   CopyGuid (&gOldFormEntry.FormSetGuid, &FormData->FormSetGuid);
   4032   gOldFormEntry.FormId    = FormData->FormId;
   4033 
   4034   //
   4035   //Free the Ui menu option list.
   4036   //
   4037   FreeMenuOptionData(&gMenuOption);
   4038 
   4039   return Status;
   4040 }
   4041 
   4042 /**
   4043   Clear Screen to the initial state.
   4044 **/
   4045 VOID
   4046 EFIAPI
   4047 DriverClearDisplayPage (
   4048   VOID
   4049   )
   4050 {
   4051   ClearDisplayPage ();
   4052   mIsFirstForm = TRUE;
   4053 }
   4054 
   4055 /**
   4056   Set Buffer to Value for Size bytes.
   4057 
   4058   @param  Buffer                 Memory to set.
   4059   @param  Size                   Number of bytes to set
   4060   @param  Value                  Value of the set operation.
   4061 
   4062 **/
   4063 VOID
   4064 SetUnicodeMem (
   4065   IN VOID   *Buffer,
   4066   IN UINTN  Size,
   4067   IN CHAR16 Value
   4068   )
   4069 {
   4070   CHAR16  *Ptr;
   4071 
   4072   Ptr = Buffer;
   4073   while ((Size--)  != 0) {
   4074     *(Ptr++) = Value;
   4075   }
   4076 }
   4077 
   4078 /**
   4079   Initialize Setup Browser driver.
   4080 
   4081   @param ImageHandle     The image handle.
   4082   @param SystemTable     The system table.
   4083 
   4084   @retval EFI_SUCCESS    The Setup Browser module is initialized correctly..
   4085   @return Other value if failed to initialize the Setup Browser module.
   4086 
   4087 **/
   4088 EFI_STATUS
   4089 EFIAPI
   4090 InitializeDisplayEngine (
   4091   IN EFI_HANDLE           ImageHandle,
   4092   IN EFI_SYSTEM_TABLE     *SystemTable
   4093   )
   4094 {
   4095   EFI_STATUS                          Status;
   4096   EFI_INPUT_KEY                       HotKey;
   4097   EFI_STRING                          NewString;
   4098   EDKII_FORM_BROWSER_EXTENSION2_PROTOCOL *FormBrowserEx2;
   4099 
   4100   //
   4101   // Publish our HII data
   4102   //
   4103   gHiiHandle = HiiAddPackages (
   4104                  &gDisplayEngineGuid,
   4105                  ImageHandle,
   4106                  DisplayEngineStrings,
   4107                  NULL
   4108                  );
   4109   ASSERT (gHiiHandle != NULL);
   4110 
   4111   //
   4112   // Install Form Display protocol
   4113   //
   4114   Status = gBS->InstallProtocolInterface (
   4115                   &mPrivateData.Handle,
   4116                   &gEdkiiFormDisplayEngineProtocolGuid,
   4117                   EFI_NATIVE_INTERFACE,
   4118                   &mPrivateData.FromDisplayProt
   4119                   );
   4120   ASSERT_EFI_ERROR (Status);
   4121 
   4122   InitializeDisplayStrings();
   4123 
   4124   ZeroMem (&gHighligthMenuInfo, sizeof (gHighligthMenuInfo));
   4125   ZeroMem (&gOldFormEntry, sizeof (gOldFormEntry));
   4126 
   4127   //
   4128   // Use BrowserEx2 protocol to register HotKey.
   4129   //
   4130   Status = gBS->LocateProtocol (&gEdkiiFormBrowserEx2ProtocolGuid, NULL, (VOID **) &FormBrowserEx2);
   4131   if (!EFI_ERROR (Status)) {
   4132     //
   4133     // Register the default HotKey F9 and F10 again.
   4134     //
   4135     HotKey.UnicodeChar = CHAR_NULL;
   4136     HotKey.ScanCode   = SCAN_F10;
   4137     NewString         = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_TEN_STRING), NULL);
   4138     ASSERT (NewString != NULL);
   4139     FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_SUBMIT, 0, NewString);
   4140 
   4141     HotKey.ScanCode   = SCAN_F9;
   4142     NewString         = HiiGetString (gHiiHandle, STRING_TOKEN (FUNCTION_NINE_STRING), NULL);
   4143     ASSERT (NewString != NULL);
   4144     FormBrowserEx2->RegisterHotKey (&HotKey, BROWSER_ACTION_DEFAULT, EFI_HII_DEFAULT_CLASS_STANDARD, NewString);
   4145   }
   4146 
   4147   return EFI_SUCCESS;
   4148 }
   4149 
   4150 /**
   4151   This is the default unload handle for display core drivers.
   4152 
   4153   @param[in]  ImageHandle       The drivers' driver image.
   4154 
   4155   @retval EFI_SUCCESS           The image is unloaded.
   4156   @retval Others                Failed to unload the image.
   4157 
   4158 **/
   4159 EFI_STATUS
   4160 EFIAPI
   4161 UnloadDisplayEngine (
   4162   IN EFI_HANDLE             ImageHandle
   4163   )
   4164 {
   4165   HiiRemovePackages(gHiiHandle);
   4166 
   4167   FreeDisplayStrings ();
   4168 
   4169   if (gHighligthMenuInfo.HLTOpCode != NULL) {
   4170     FreePool (gHighligthMenuInfo.HLTOpCode);
   4171   }
   4172 
   4173   if (gHighligthMenuInfo.TOSOpCode != NULL) {
   4174     FreePool (gHighligthMenuInfo.TOSOpCode);
   4175   }
   4176 
   4177   return EFI_SUCCESS;
   4178 }
   4179