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