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