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 *
GetToken(IN EFI_STRING_ID Token,IN EFI_HII_HANDLE HiiHandle)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
InitializeDisplayStrings(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
FreeDisplayStrings(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
GetPrompt(IN EFI_IFR_OP_HEADER * OpCode)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
GetWidth(IN UI_MENU_OPTION * MenuOption,OUT UINT16 * AdjustWidth)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
GetLineByWidth(IN CHAR16 * InputString,IN UINT16 LineWidth,IN OUT UINT16 * GlyphWidth,IN OUT UINTN * Index,OUT CHAR16 ** OutputString)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
UiAddMenuOption(IN FORM_DISPLAY_ENGINE_STATEMENT * Statement,IN UINT16 * MenuItemCount,IN BOOLEAN NestIn)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
ConvertStatementToMenu(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
GetStringWidth(IN CHAR16 * String)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
UpdateSkipInfoForMenu(IN UI_MENU_OPTION * MenuOption,IN CHAR16 * OptionString)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
UpdateOptionSkipLines(IN UI_MENU_OPTION * MenuOption)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
PrintableMenu(UI_MENU_OPTION * MenuOption)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
IsSelectable(UI_MENU_OPTION * MenuOption)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
MoveToNextStatement(IN BOOLEAN GoUp,IN OUT LIST_ENTRY ** CurrentPosition,IN UINTN GapToTop,IN BOOLEAN FindInForm)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
ProcessStringForDateTime(UI_MENU_OPTION * MenuOption,CHAR16 * OptionString,BOOLEAN AddOptCol)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
AdjustDateAndTimePosition(IN BOOLEAN DirectionUp,IN OUT LIST_ENTRY ** CurrentPosition)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
GetFieldFromNum(IN EFI_IFR_OP_HEADER * OpCode)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 *
GetHotKeyFromRegisterList(IN EFI_INPUT_KEY * KeyData)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
ValueIsScroll(IN BOOLEAN Direction,IN LIST_ENTRY * CurrentPos)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
UiWaitForEvent(IN EFI_EVENT Event)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
GetQuestionIdInfo(IN EFI_IFR_OP_HEADER * OpCode)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 *
FindTopOfScreenMenu(IN LIST_ENTRY * CurPos,IN UINTN Rows,OUT UINTN * SkipValue)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
GetIndexInfoForOpcode(IN EFI_IFR_OP_HEADER * OpCode)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
IsSavedHighlightStatement(IN FORM_DISPLAY_ENGINE_STATEMENT * HighLightedStatement)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
IsHighLightMenuOption(IN UI_MENU_OPTION * MenuOption)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 *
FindHighLightMenuOption(IN FORM_DISPLAY_ENGINE_STATEMENT * HighLightedStatement)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
IsTopOfScreeMenuOption(IN UI_MENU_OPTION * MenuOption)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 *
FindTopOfScreenMenuOption(VOID)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
FindTopMenu(IN FORM_DISPLAY_ENGINE_FORM * FormData,OUT LIST_ENTRY ** TopOfScreen,OUT LIST_ENTRY ** HighlightMenu,OUT UINTN * SkipValue)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
UpdateHighlightMenuInfo(IN LIST_ENTRY * Highlight,IN LIST_ENTRY * TopOfScreen,IN UINTN SkipValue)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
SetDisplayAttribute(IN UI_MENU_OPTION * MenuOption,IN BOOLEAN Highlight)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
DisplayMenuString(IN UI_MENU_OPTION * MenuOption,IN UINTN Col,IN UINTN Row,IN CHAR16 * String,IN UINTN Width,IN BOOLEAN Highlight)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
HasOptionString(IN UI_MENU_OPTION * MenuOption)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
FxConfirmPopup(IN UINT32 Action)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
DisplayOneMenu(IN UI_MENU_OPTION * MenuOption,IN UINTN SkipWidth,IN UINTN BeginCol,IN UINTN SkipLine,IN UINTN BottomRow,IN BOOLEAN Highlight,IN BOOLEAN UpdateCol)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
UiDisplayMenu(IN FORM_DISPLAY_ENGINE_FORM * FormData)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
BrowserStatusProcess(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
FormDisplay(IN FORM_DISPLAY_ENGINE_FORM * FormData,OUT USER_INPUT * UserInputData)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
DriverClearDisplayPage(VOID)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
SetUnicodeMem(IN VOID * Buffer,IN UINTN Size,IN CHAR16 Value)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
InitializeDisplayEngine(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)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
UnloadDisplayEngine(IN EFI_HANDLE ImageHandle)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