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