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