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