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