2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 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
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.
17 LIST_ENTRY gMenuOption
;
19 MENU_REFRESH_ENTRY
*gMenuRefreshHead
; // Menu list used for refresh timer opcode.
20 MENU_REFRESH_ENTRY
*gMenuEventGuidRefreshHead
; // Menu list used for refresh event guid opcode.
23 // Search table for UiDisplayMenu()
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
56 UINTN mScanCodeNumber
= sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]);
58 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
102 BOOLEAN GetLineByWidthFinished
= FALSE
;
106 Set Buffer to Value for Size bytes.
108 @param Buffer Memory to set.
109 @param Size Number of bytes to set
110 @param Value Value of the set operation.
123 while ((Size
--) != 0) {
130 Initialize Menu option list.
138 InitializeListHead (&gMenuOption
);
143 Free Menu option linked list.
151 UI_MENU_OPTION
*MenuOption
;
153 while (!IsListEmpty (&gMenuOption
)) {
154 MenuOption
= MENU_OPTION_FROM_LINK (gMenuOption
.ForwardLink
);
155 RemoveEntryList (&MenuOption
->Link
);
158 // We allocated space for this description when we did a GetToken, free it here
160 if (MenuOption
->Skip
!= 0) {
162 // For date/time, MenuOption->Description is shared by three Menu Options
163 // Data format : [01/02/2004] [11:22:33]
164 // Line number : 0 0 1 0 0 1
166 FreePool (MenuOption
->Description
);
168 FreePool (MenuOption
);
174 Create a menu with specified formset GUID and form ID, and add it as a child
175 of the given parent menu.
177 @param Parent The parent of menu to be added.
178 @param HiiHandle Hii handle related to this formset.
179 @param FormSetGuid The Formset Guid of menu to be added.
180 @param FormId The Form ID of menu to be added.
182 @return A pointer to the newly added menu or NULL if memory is insufficient.
187 IN OUT UI_MENU_LIST
*Parent
,
188 IN EFI_HII_HANDLE HiiHandle
,
189 IN EFI_GUID
*FormSetGuid
,
193 UI_MENU_LIST
*MenuList
;
195 MenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
196 if (MenuList
== NULL
) {
200 MenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
201 InitializeListHead (&MenuList
->ChildListHead
);
203 MenuList
->HiiHandle
= HiiHandle
;
204 CopyMem (&MenuList
->FormSetGuid
, FormSetGuid
, sizeof (EFI_GUID
));
205 MenuList
->FormId
= FormId
;
206 MenuList
->Parent
= Parent
;
208 if (Parent
== NULL
) {
210 // If parent is not specified, it is the root Form of a Formset
212 InsertTailList (&gMenuList
, &MenuList
->Link
);
214 InsertTailList (&Parent
->ChildListHead
, &MenuList
->Link
);
222 Search Menu with given FormId, FormSetGuid and Handle in all cached menu list.
224 @param Parent The parent of menu to search.
225 @param Handle Hii handle related to this formset.
226 @param FormSetGuid The Formset GUID of the menu to search.
227 @param FormId The Form ID of menu to search.
229 @return A pointer to menu found or NULL if not found.
233 UiFindChildMenuList (
234 IN UI_MENU_LIST
*Parent
,
235 IN EFI_HII_HANDLE Handle
,
236 IN EFI_GUID
*FormSetGuid
,
242 UI_MENU_LIST
*MenuList
;
244 ASSERT (Parent
!= NULL
);
246 if (Parent
->FormId
== FormId
&& CompareGuid (FormSetGuid
, &Parent
->FormSetGuid
) && Parent
->HiiHandle
== Handle
) {
250 Link
= GetFirstNode (&Parent
->ChildListHead
);
251 while (!IsNull (&Parent
->ChildListHead
, Link
)) {
252 Child
= UI_MENU_LIST_FROM_LINK (Link
);
254 MenuList
= UiFindChildMenuList (Child
, Handle
, FormSetGuid
, FormId
);
255 if (MenuList
!= NULL
) {
259 Link
= GetNextNode (&Parent
->ChildListHead
, Link
);
267 Search Menu with given Handle, FormSetGuid and FormId in all cached menu list.
269 @param Handle Hii handle related to this formset.
270 @param FormSetGuid The Formset GUID of the menu to search.
271 @param FormId The Form ID of menu to search.
273 @return A pointer to menu found or NULL if not found.
278 IN EFI_HII_HANDLE Handle
,
279 IN EFI_GUID
*FormSetGuid
,
284 UI_MENU_LIST
*MenuList
;
287 Link
= GetFirstNode (&gMenuList
);
288 while (!IsNull (&gMenuList
, Link
)) {
289 MenuList
= UI_MENU_LIST_FROM_LINK (Link
);
291 Child
= UiFindChildMenuList(MenuList
, Handle
, FormSetGuid
, FormId
);
295 // If this form already in the menu history list,
296 // just free the list between old this form.
298 UiFreeMenuList(&Child
->ChildListHead
);
302 Link
= GetNextNode (&gMenuList
, Link
);
309 Free Menu list linked list.
311 @param MenuListHead One Menu list point in the menu list.
316 LIST_ENTRY
*MenuListHead
319 UI_MENU_LIST
*MenuList
;
321 while (!IsListEmpty (MenuListHead
)) {
322 MenuList
= UI_MENU_LIST_FROM_LINK (MenuListHead
->ForwardLink
);
323 RemoveEntryList (&MenuList
->Link
);
325 UiFreeMenuList(&MenuList
->ChildListHead
);
332 Free Menu option linked list.
340 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
342 while (gMenuRefreshHead
!= NULL
) {
343 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
344 FreePool (gMenuRefreshHead
);
345 gMenuRefreshHead
= OldMenuRefreshEntry
;
348 while (gMenuEventGuidRefreshHead
!= NULL
) {
349 OldMenuRefreshEntry
= gMenuEventGuidRefreshHead
->Next
;
350 if (gMenuEventGuidRefreshHead
!= NULL
) {
351 gBS
->CloseEvent(gMenuEventGuidRefreshHead
->Event
);
353 FreePool (gMenuEventGuidRefreshHead
);
354 gMenuEventGuidRefreshHead
= OldMenuRefreshEntry
;
360 Process option string for date/time opcode.
362 @param MenuOption Menu option point to date/time.
363 @param OptionString Option string input for process.
364 @param AddOptCol Whether need to update MenuOption->OptCol.
368 ProcessStringForDateTime (
369 UI_MENU_OPTION
*MenuOption
,
370 CHAR16
*OptionString
,
376 FORM_BROWSER_STATEMENT
*Statement
;
378 ASSERT (MenuOption
!= NULL
&& OptionString
!= NULL
);
380 Statement
= MenuOption
->ThisTag
;
383 // If leading spaces on OptionString - remove the spaces
385 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
387 // Base on the blockspace to get the option column info.
390 MenuOption
->OptCol
++;
394 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
395 OptionString
[Count
] = OptionString
[Index
];
398 OptionString
[Count
] = CHAR_NULL
;
401 // Enable to suppress field in the opcode base on the flag.
403 if (Statement
->Operand
== EFI_IFR_DATE_OP
) {
405 // OptionString format is: <**: **: ****>
409 if ((Statement
->Flags
& EFI_QF_DATE_MONTH_SUPPRESS
) && (MenuOption
->Sequence
== 0)) {
411 // At this point, only "<**:" in the optionstring.
412 // Clean the day's ** field, after clean, the format is "< :"
414 SetUnicodeMem (&OptionString
[1], 2, L
' ');
415 } else if ((Statement
->Flags
& EFI_QF_DATE_DAY_SUPPRESS
) && (MenuOption
->Sequence
== 1)) {
417 // At this point, only "**:" in the optionstring.
418 // Clean the month's "**" field, after clean, the format is " :"
420 SetUnicodeMem (&OptionString
[0], 2, L
' ');
421 } else if ((Statement
->Flags
& EFI_QF_DATE_YEAR_SUPPRESS
) && (MenuOption
->Sequence
== 2)) {
423 // At this point, only "****>" in the optionstring.
424 // Clean the year's "****" field, after clean, the format is " >"
426 SetUnicodeMem (&OptionString
[0], 4, L
' ');
428 } else if (Statement
->Operand
== EFI_IFR_TIME_OP
) {
430 // OptionString format is: <**: **: **>
431 // |hour|minute|second|
434 if ((Statement
->Flags
& QF_TIME_HOUR_SUPPRESS
) && (MenuOption
->Sequence
== 0)) {
436 // At this point, only "<**:" in the optionstring.
437 // Clean the hour's ** field, after clean, the format is "< :"
439 SetUnicodeMem (&OptionString
[1], 2, L
' ');
440 } else if ((Statement
->Flags
& QF_TIME_MINUTE_SUPPRESS
) && (MenuOption
->Sequence
== 1)) {
442 // At this point, only "**:" in the optionstring.
443 // Clean the minute's "**" field, after clean, the format is " :"
445 SetUnicodeMem (&OptionString
[0], 2, L
' ');
446 } else if ((Statement
->Flags
& QF_TIME_SECOND_SUPPRESS
) && (MenuOption
->Sequence
== 2)) {
448 // At this point, only "**>" in the optionstring.
449 // Clean the second's "**" field, after clean, the format is " >"
451 SetUnicodeMem (&OptionString
[0], 2, L
' ');
459 @param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
463 IN MENU_REFRESH_ENTRY
*MenuRefreshEntry
466 CHAR16
*OptionString
;
468 UI_MENU_SELECTION
*Selection
;
469 FORM_BROWSER_STATEMENT
*Question
;
471 Selection
= MenuRefreshEntry
->Selection
;
472 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
474 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, GetSetValueWithHiiDriver
);
475 if (EFI_ERROR (Status
)) {
480 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
482 if (OptionString
!= NULL
) {
484 // If old Text is longer than new string, need to clean the old string before paint the newer.
485 // This option is no need for time/date opcode, because time/data opcode has fixed string length.
487 if ((MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_DATE_OP
) &&
488 (MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_TIME_OP
)) {
490 MenuRefreshEntry
->CurrentColumn
,
491 MenuRefreshEntry
->CurrentColumn
+ gOptionBlockWidth
- 1,
492 MenuRefreshEntry
->CurrentRow
,
493 MenuRefreshEntry
->CurrentRow
,
494 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
498 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
499 ProcessStringForDateTime(MenuRefreshEntry
->MenuOption
, OptionString
, FALSE
);
500 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
501 FreePool (OptionString
);
505 // Question value may be changed, need invoke its Callback()
507 Status
= ProcessCallBackFunction (Selection
, Question
, EFI_BROWSER_ACTION_CHANGING
, FALSE
);
513 Refresh the question which has refresh guid event attribute.
515 @param Event The event which has this function related.
516 @param Context The input context info related to this event or the status code return to the caller.
520 RefreshQuestionNotify(
525 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
526 UI_MENU_SELECTION
*Selection
;
529 // Reset FormPackage update flag
531 mHiiPackageListUpdated
= FALSE
;
533 MenuRefreshEntry
= (MENU_REFRESH_ENTRY
*)Context
;
534 ASSERT (MenuRefreshEntry
!= NULL
);
535 Selection
= MenuRefreshEntry
->Selection
;
537 RefreshQuestion (MenuRefreshEntry
);
539 if (mHiiPackageListUpdated
) {
541 // Package list is updated, force to reparse IFR binary of target Formset
543 mHiiPackageListUpdated
= FALSE
;
544 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
558 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
560 UI_MENU_SELECTION
*Selection
;
562 if (gMenuRefreshHead
!= NULL
) {
564 // call from refresh interval process.
566 MenuRefreshEntry
= gMenuRefreshHead
;
567 Selection
= MenuRefreshEntry
->Selection
;
569 // Reset FormPackage update flag
571 mHiiPackageListUpdated
= FALSE
;
574 Status
= RefreshQuestion (MenuRefreshEntry
);
575 if (EFI_ERROR (Status
)) {
579 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
581 } while (MenuRefreshEntry
!= NULL
);
583 if (mHiiPackageListUpdated
) {
585 // Package list is updated, force to reparse IFR binary of target Formset
587 mHiiPackageListUpdated
= FALSE
;
588 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
598 Wait for a given event to fire, or for an optional timeout to expire.
600 @param Event The event to wait for
601 @param Timeout An optional timeout value in 100 ns units.
602 @param RefreshInterval Menu refresh interval (in seconds).
604 @retval EFI_SUCCESS Event fired before Timeout expired.
605 @retval EFI_TIME_OUT Timout expired before Event fired.
609 UiWaitForSingleEvent (
611 IN UINT64 Timeout
, OPTIONAL
612 IN UINT8 RefreshInterval OPTIONAL
617 EFI_EVENT TimerEvent
;
618 EFI_EVENT WaitList
[2];
622 // Create a timer event
624 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
625 if (!EFI_ERROR (Status
)) {
627 // Set the timer event
636 // Wait for the original event or the timer
639 WaitList
[1] = TimerEvent
;
640 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
641 gBS
->CloseEvent (TimerEvent
);
644 // If the timer expired, change the return to timed out
646 if (!EFI_ERROR (Status
) && Index
== 1) {
647 Status
= EFI_TIMEOUT
;
652 // Update screen every second
654 if (RefreshInterval
== 0) {
655 Timeout
= ONE_SECOND
;
657 Timeout
= RefreshInterval
* ONE_SECOND
;
661 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
664 // Set the timer event
673 // Wait for the original event or the timer
676 WaitList
[1] = TimerEvent
;
677 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
680 // If the timer expired, update anything that needs a refresh and keep waiting
682 if (!EFI_ERROR (Status
) && Index
== 1) {
683 Status
= EFI_TIMEOUT
;
684 if (RefreshInterval
!= 0) {
685 Status
= RefreshForm ();
689 gBS
->CloseEvent (TimerEvent
);
690 } while (Status
== EFI_TIMEOUT
);
698 Add one menu option by specified description and context.
700 @param String String description for this option.
701 @param Handle Hii handle for the package list.
702 @param Form The form this statement belong to.
703 @param Statement Statement of this Menu Option.
704 @param NumberOfLines Display lines for this Menu Option.
705 @param MenuItemCount The index for this Option in the Menu.
707 @retval Pointer Pointer to the added Menu Option.
713 IN EFI_HII_HANDLE Handle
,
714 IN FORM_BROWSER_FORM
*Form
,
715 IN FORM_BROWSER_STATEMENT
*Statement
,
716 IN UINT16 NumberOfLines
,
717 IN UINT16 MenuItemCount
720 UI_MENU_OPTION
*MenuOption
;
727 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
729 // Add three MenuOptions for Date/Time
730 // Data format : [01/02/2004] [11:22:33]
731 // Line number : 0 0 1 0 0 1
736 if (Statement
->Storage
== NULL
) {
738 // For RTC type of date/time, set default refresh interval to be 1 second
740 if (Statement
->RefreshInterval
== 0) {
741 Statement
->RefreshInterval
= 1;
746 for (Index
= 0; Index
< Count
; Index
++) {
747 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
750 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
751 MenuOption
->Description
= String
;
752 MenuOption
->Handle
= Handle
;
753 MenuOption
->ThisTag
= Statement
;
754 MenuOption
->EntryNumber
= MenuItemCount
;
758 // Override LineNumber for the MenuOption in Date/Time sequence
760 MenuOption
->Skip
= 1;
762 MenuOption
->Skip
= NumberOfLines
;
764 MenuOption
->Sequence
= Index
;
766 if (EvaluateExpressionList(Statement
->Expression
, FALSE
, NULL
, NULL
) == ExpressGrayOut
) {
767 MenuOption
->GrayOut
= TRUE
;
769 MenuOption
->GrayOut
= FALSE
;
773 // If the form or the question has the lock attribute, deal same as grayout.
775 if (Form
->Locked
|| Statement
->Locked
) {
776 MenuOption
->GrayOut
= TRUE
;
779 switch (Statement
->Operand
) {
780 case EFI_IFR_ORDERED_LIST_OP
:
781 case EFI_IFR_ONE_OF_OP
:
782 case EFI_IFR_NUMERIC_OP
:
783 case EFI_IFR_TIME_OP
:
784 case EFI_IFR_DATE_OP
:
785 case EFI_IFR_CHECKBOX_OP
:
786 case EFI_IFR_PASSWORD_OP
:
787 case EFI_IFR_STRING_OP
:
789 // User could change the value of these items
791 MenuOption
->IsQuestion
= TRUE
;
794 case EFI_IFR_TEXT_OP
:
795 if (FeaturePcdGet (PcdBrowserGrayOutTextStatement
)) {
797 // Initializing GrayOut option as TRUE for Text setup options
798 // so that those options will be Gray in colour and un selectable.
800 MenuOption
->GrayOut
= TRUE
;
803 // break skipped on purpose
806 MenuOption
->IsQuestion
= FALSE
;
810 if ((Statement
->ValueExpression
!= NULL
) ||
811 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
812 MenuOption
->ReadOnly
= TRUE
;
813 if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu
)) {
814 MenuOption
->GrayOut
= TRUE
;
818 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
826 Routine used to abstract a generic dialog interface and return the selected key or string
828 @param NumberOfLines The number of lines for the dialog box
829 @param HotKey Defines whether a single character is parsed
830 (TRUE) and returned in KeyValue or a string is
831 returned in StringBuffer. Two special characters
832 are considered when entering a string, a SCAN_ESC
833 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
834 string input and returns
835 @param MaximumStringSize The maximum size in bytes of a typed in string
836 (each character is a CHAR16) and the minimum
837 string returned is two bytes
838 @param StringBuffer The passed in pointer to the buffer which will
839 hold the typed in string if HotKey is FALSE
840 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
841 @param ... A series of (quantity == NumberOfLines) text
842 strings which will be used to construct the dialog
845 @retval EFI_SUCCESS Displayed dialog and received user interaction
846 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
847 (StringBuffer == NULL) && (HotKey == FALSE))
848 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
854 IN UINTN NumberOfLines
,
856 IN UINTN MaximumStringSize
,
857 OUT CHAR16
*StringBuffer
,
858 OUT EFI_INPUT_KEY
*KeyValue
,
867 CHAR16
*BufferedString
;
874 BOOLEAN SelectionComplete
;
876 UINTN CurrentAttribute
;
877 UINTN DimensionsWidth
;
878 UINTN DimensionsHeight
;
880 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
881 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
883 SelectionComplete
= FALSE
;
885 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
886 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
887 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
890 ASSERT (BufferedString
);
893 // Zero the outgoing buffer
895 ZeroMem (StringBuffer
, MaximumStringSize
);
898 if (KeyValue
== NULL
) {
899 return EFI_INVALID_PARAMETER
;
902 if (StringBuffer
== NULL
) {
903 return EFI_INVALID_PARAMETER
;
909 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
913 VA_START (Marker
, KeyValue
);
916 // Determine the largest string in the dialog box
917 // Notice we are starting with 1 since String is the first string
919 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
920 StackString
= VA_ARG (Marker
, CHAR16
*);
922 if (StackString
[0] == L
' ') {
923 InputOffset
= Count
+ 1;
926 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
928 // Size of the string visually and subtract the width by one for the null-terminator
930 LargestString
= (GetStringWidth (StackString
) / 2);
935 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
936 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
943 VA_START (Marker
, KeyValue
);
944 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
948 // Take the first key typed and report it back?
951 Status
= WaitForKeyStroke (&Key
);
952 ASSERT_EFI_ERROR (Status
);
953 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
957 Status
= WaitForKeyStroke (&Key
);
959 switch (Key
.UnicodeChar
) {
961 switch (Key
.ScanCode
) {
963 FreePool (TempString
);
964 FreePool (BufferedString
);
965 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
966 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
967 return EFI_DEVICE_ERROR
;
975 case CHAR_CARRIAGE_RETURN
:
976 SelectionComplete
= TRUE
;
977 FreePool (TempString
);
978 FreePool (BufferedString
);
979 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
980 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
985 if (StringBuffer
[0] != CHAR_NULL
) {
986 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
987 TempString
[Index
] = StringBuffer
[Index
];
990 // Effectively truncate string by 1 character
992 TempString
[Index
- 1] = CHAR_NULL
;
993 StrCpy (StringBuffer
, TempString
);
996 // break skipped on purpose
1001 // If it is the beginning of the string, don't worry about checking maximum limits
1003 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
1004 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
1005 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
1006 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
1007 KeyPad
[0] = Key
.UnicodeChar
;
1008 KeyPad
[1] = CHAR_NULL
;
1009 StrCat (StringBuffer
, KeyPad
);
1010 StrCat (TempString
, KeyPad
);
1013 // If the width of the input string is now larger than the screen, we nee to
1014 // adjust the index to start printing portions of the string
1016 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
1018 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
1020 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
1021 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
1026 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
1027 BufferedString
[Count
] = StringBuffer
[Index
];
1030 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
1033 } while (!SelectionComplete
);
1036 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
1037 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1042 Draw a pop up windows based on the dimension, number of lines and
1045 @param RequestedWidth The width of the pop-up.
1046 @param NumberOfLines The number of lines.
1047 @param Marker The variable argument list for the list of string to be printed.
1052 IN UINTN RequestedWidth
,
1053 IN UINTN NumberOfLines
,
1065 UINTN DimensionsWidth
;
1066 UINTN DimensionsHeight
;
1068 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
1069 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
1071 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1073 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
1074 RequestedWidth
= DimensionsWidth
- 2;
1078 // Subtract the PopUp width from total Columns, allow for one space extra on
1079 // each end plus a border.
1081 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
1082 End
= Start
+ RequestedWidth
+ 1;
1084 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
1085 Bottom
= Top
+ NumberOfLines
+ 2;
1087 Character
= BOXDRAW_DOWN_RIGHT
;
1088 PrintCharAt (Start
, Top
, Character
);
1089 Character
= BOXDRAW_HORIZONTAL
;
1090 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1091 PrintChar (Character
);
1094 Character
= BOXDRAW_DOWN_LEFT
;
1095 PrintChar (Character
);
1096 Character
= BOXDRAW_VERTICAL
;
1099 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
1100 String
= VA_ARG (Marker
, CHAR16
*);
1103 // This will clear the background of the line - we never know who might have been
1104 // here before us. This differs from the next clear in that it used the non-reverse
1105 // video for normal printing.
1107 if (GetStringWidth (String
) / 2 > 1) {
1108 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1112 // Passing in a space results in the assumption that this is where typing will occur
1114 if (String
[0] == L
' ') {
1115 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
1119 // Passing in a NULL results in a blank space
1121 if (String
[0] == CHAR_NULL
) {
1122 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1126 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
1130 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1131 PrintCharAt (Start
, Index
+ 1, Character
);
1132 PrintCharAt (End
- 1, Index
+ 1, Character
);
1135 Character
= BOXDRAW_UP_RIGHT
;
1136 PrintCharAt (Start
, Bottom
- 1, Character
);
1137 Character
= BOXDRAW_HORIZONTAL
;
1138 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1139 PrintChar (Character
);
1142 Character
= BOXDRAW_UP_LEFT
;
1143 PrintChar (Character
);
1147 Draw a pop up windows based on the dimension, number of lines and
1150 @param RequestedWidth The width of the pop-up.
1151 @param NumberOfLines The number of lines.
1152 @param ... A series of text strings that displayed in the pop-up.
1157 CreateMultiStringPopUp (
1158 IN UINTN RequestedWidth
,
1159 IN UINTN NumberOfLines
,
1165 VA_START (Marker
, NumberOfLines
);
1167 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1174 Update status bar on the bottom of menu.
1176 @param Selection Current Selction info.
1177 @param MessageType The type of message to be shown.
1178 @param Flags The flags in Question header.
1179 @param State Set or clear.
1184 IN UI_MENU_SELECTION
*Selection
,
1185 IN UINTN MessageType
,
1191 CHAR16
*NvUpdateMessage
;
1192 CHAR16
*InputErrorMessage
;
1194 FORM_BROWSER_FORMSET
*LocalFormSet
;
1195 FORM_BROWSER_STATEMENT
*Question
;
1197 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1198 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1200 switch (MessageType
) {
1203 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1205 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1206 gScreenDimensions
.BottomRow
- 1,
1211 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1212 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1213 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1216 mInputError
= FALSE
;
1220 case NV_UPDATE_REQUIRED
:
1222 // Global setting support. Show configuration change on every form.
1225 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1227 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1228 Question
= Selection
->Statement
;
1229 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1231 // Update only for Question value that need to be saved into Storage.
1233 Selection
->Form
->NvUpdateRequired
= TRUE
;
1237 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1238 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1240 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1241 gScreenDimensions
.BottomRow
- 1,
1246 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1247 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1249 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1250 gScreenDimensions
.BottomRow
- 1,
1257 case REFRESH_STATUS_BAR
:
1259 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1262 switch (gBrowserSettingScope
) {
1265 // Check the maintain list to see whether there is any change.
1267 Link
= GetFirstNode (&gBrowserFormSetList
);
1268 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1269 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1270 if (IsNvUpdateRequired(LocalFormSet
)) {
1271 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1274 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1279 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1290 FreePool (InputErrorMessage
);
1291 FreePool (NvUpdateMessage
);
1297 Get the supported width for a particular op-code
1299 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1300 @param Handle The handle in the HII database being used
1302 @return Returns the number of CHAR16 characters that is support.
1307 IN FORM_BROWSER_STATEMENT
*Statement
,
1308 IN EFI_HII_HANDLE Handle
1318 // See if the second text parameter is really NULL
1320 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1321 String
= GetToken (Statement
->TextTwo
, Handle
);
1322 Size
= StrLen (String
);
1326 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1327 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1328 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1329 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1330 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1332 // Allow a wide display if text op-code and no secondary text op-code
1334 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1336 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1338 Width
= (UINT16
) gPromptBlockWidth
;
1341 if (Statement
->InSubtitle
) {
1342 Width
-= SUBTITLE_INDENT
;
1345 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1349 Will copy LineWidth amount of a string in the OutputString buffer and return the
1350 number of CHAR16 characters that were copied into the OutputString buffer.
1351 The output string format is:
1352 Glyph Info + String info + '\0'.
1354 In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
1356 @param InputString String description for this option.
1357 @param LineWidth Width of the desired string to extract in CHAR16
1359 @param GlyphWidth The glyph width of the begin of the char in the string.
1360 @param Index Where in InputString to start the copy process
1361 @param OutputString Buffer to copy the string into
1363 @return Returns the number of CHAR16 characters that were copied into the OutputString
1364 buffer, include extra glyph info and '\0' info.
1369 IN CHAR16
*InputString
,
1370 IN UINT16 LineWidth
,
1371 IN OUT UINT16
*GlyphWidth
,
1372 IN OUT UINTN
*Index
,
1373 OUT CHAR16
**OutputString
1378 UINT16 OriginalGlyphWidth
;
1380 UINT16 LastSpaceOffset
;
1381 UINT16 LastGlyphWidth
;
1383 if (InputString
== NULL
|| Index
== NULL
|| OutputString
== NULL
) {
1387 if (LineWidth
== 0 || *GlyphWidth
== 0) {
1392 // Save original glyph width.
1394 OriginalGlyphWidth
= *GlyphWidth
;
1395 LastGlyphWidth
= OriginalGlyphWidth
;
1397 LastSpaceOffset
= 0;
1400 // 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.
1401 // To avoid displaying this empty line in screen, just skip the two CHARs here.
1403 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1404 *Index
= *Index
+ 2;
1408 // Fast-forward the string and see if there is a carriage-return in the string
1410 for (StrOffset
= 0, GlyphOffset
= 0; GlyphOffset
<= LineWidth
; StrOffset
++) {
1411 switch (InputString
[*Index
+ StrOffset
]) {
1420 case CHAR_CARRIAGE_RETURN
:
1427 GlyphOffset
= GlyphOffset
+ *GlyphWidth
;
1430 // Record the last space info in this line. Will be used in rewind.
1432 if ((InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) && (GlyphOffset
<= LineWidth
)) {
1433 LastSpaceOffset
= StrOffset
;
1434 LastGlyphWidth
= *GlyphWidth
;
1445 // Rewind the string from the maximum size until we see a space to break the line
1447 if (GlyphOffset
> LineWidth
) {
1449 // Rewind the string to last space char in this line.
1451 if (LastSpaceOffset
!= 0) {
1452 StrOffset
= LastSpaceOffset
;
1453 *GlyphWidth
= LastGlyphWidth
;
1456 // Roll back to last char in the line width.
1463 // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
1465 if (StrOffset
== 0 && (InputString
[*Index
+ StrOffset
] == CHAR_NULL
)) {
1470 // Need extra glyph info and '\0' info, so +2.
1472 *OutputString
= AllocateZeroPool (((UINTN
) (StrOffset
+ 2) * sizeof(CHAR16
)));
1473 if (*OutputString
== NULL
) {
1478 // Save the glyph info at the begin of the string, will used by Print function.
1480 if (OriginalGlyphWidth
== 1) {
1481 *(*OutputString
) = NARROW_CHAR
;
1483 *(*OutputString
) = WIDE_CHAR
;
1486 CopyMem ((*OutputString
) + 1, &InputString
[*Index
], StrOffset
* sizeof(CHAR16
));
1488 if (InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) {
1490 // Skip the space info at the begin of next line.
1492 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1493 } else if (InputString
[*Index
+ StrOffset
] == CHAR_LINEFEED
) {
1495 // Skip the /n or /n/r info.
1497 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_CARRIAGE_RETURN
) {
1498 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1500 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1502 } else if (InputString
[*Index
+ StrOffset
] == CHAR_CARRIAGE_RETURN
) {
1504 // Skip the /r or /r/n info.
1506 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_LINEFEED
) {
1507 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1509 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1512 *Index
= (UINT16
) (*Index
+ StrOffset
);
1516 // Include extra glyph info and '\0' info, so +2.
1518 return StrOffset
+ 2;
1523 Update display lines for a Menu Option.
1525 @param Selection The user's selection.
1526 @param MenuOption The MenuOption to be checked.
1530 UpdateOptionSkipLines (
1531 IN UI_MENU_SELECTION
*Selection
,
1532 IN UI_MENU_OPTION
*MenuOption
1539 CHAR16
*OutputString
;
1540 CHAR16
*OptionString
;
1544 OptionString
= NULL
;
1545 Width
= (UINT16
) gOptionBlockWidth
;
1549 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1550 if (OptionString
== NULL
) {
1554 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
1556 // If there is more string to process print on the next row and increment the Skip value
1558 if (StrLen (&OptionString
[Index
]) != 0) {
1561 // Since the Number of lines for this menu entry may or may not be reflected accurately
1562 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1563 // some testing to ensure we are keeping this in-sync.
1565 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1567 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1572 FreePool (OutputString
);
1575 if (OptionString
!= NULL
) {
1576 FreePool (OptionString
);
1582 Check whether this Menu Option could be highlighted.
1584 This is an internal function.
1586 @param MenuOption The MenuOption to be checked.
1588 @retval TRUE This Menu Option is selectable.
1589 @retval FALSE This Menu Option could not be selected.
1594 UI_MENU_OPTION
*MenuOption
1597 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1598 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1607 Determine if the menu is the last menu that can be selected.
1609 This is an internal function.
1611 @param Direction The scroll direction. False is down. True is up.
1612 @param CurrentPos The current focus.
1614 @return FALSE -- the menu isn't the last menu that can be selected.
1615 @return TRUE -- the menu is the last menu that can be selected.
1620 IN BOOLEAN Direction
,
1621 IN LIST_ENTRY
*CurrentPos
1626 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1628 if (Temp
== &gMenuOption
) {
1637 Move to next selectable statement.
1639 This is an internal function.
1641 @param Selection Menu selection.
1642 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1643 @param CurrentPosition Current position.
1644 @param GapToTop Gap position to top or bottom.
1646 @return The row distance from current MenuOption to next selectable MenuOption.
1648 @retval -1 Reach the begin of the menu, still can't find the selectable menu.
1649 @retval Value Find the selectable menu, maybe the truly selectable, maybe the l
1650 last menu showing at current form.
1654 MoveToNextStatement (
1655 IN UI_MENU_SELECTION
*Selection
,
1657 IN OUT LIST_ENTRY
**CurrentPosition
,
1663 UI_MENU_OPTION
*NextMenuOption
;
1664 UI_MENU_OPTION
*PreMenuOption
;
1667 Pos
= *CurrentPosition
;
1668 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1671 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1673 // NextMenuOption->Row == 0 means this menu has not calculate
1674 // the NextMenuOption->Skip value yet, just calculate here.
1676 if (NextMenuOption
->Row
== 0) {
1677 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1680 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1682 // In this case, still can't find the selectable menu,
1683 // return the last one in the showing form.
1685 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1686 NextMenuOption
= PreMenuOption
;
1691 // Current Position doesn't need to be caculated when go up.
1692 // Caculate distanct at first when go up
1694 Distance
+= NextMenuOption
->Skip
;
1697 if (IsSelectable (NextMenuOption
)) {
1702 // Arrive at begin of the menu list.
1704 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1711 // In this case, still can't find the selectable menu,
1712 // return the last one in the showing form.
1714 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1715 NextMenuOption
= PreMenuOption
;
1719 Distance
+= NextMenuOption
->Skip
;
1722 PreMenuOption
= NextMenuOption
;
1723 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1726 *CurrentPosition
= &NextMenuOption
->Link
;
1732 Adjust Data and Time position accordingly.
1733 Data format : [01/02/2004] [11:22:33]
1734 Line number : 0 0 1 0 0 1
1736 This is an internal function.
1738 @param DirectionUp the up or down direction. False is down. True is
1740 @param CurrentPosition Current position. On return: Point to the last
1741 Option (Year or Second) if up; Point to the first
1742 Option (Month or Hour) if down.
1744 @return Return line number to pad. It is possible that we stand on a zero-advance
1745 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1749 AdjustDateAndTimePosition (
1750 IN BOOLEAN DirectionUp
,
1751 IN OUT LIST_ENTRY
**CurrentPosition
1755 LIST_ENTRY
*NewPosition
;
1756 UI_MENU_OPTION
*MenuOption
;
1757 UINTN PadLineNumber
;
1760 NewPosition
= *CurrentPosition
;
1761 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1763 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1764 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1766 // Calculate the distance from current position to the last Date/Time MenuOption
1769 while (MenuOption
->Skip
== 0) {
1771 NewPosition
= NewPosition
->ForwardLink
;
1772 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1776 NewPosition
= *CurrentPosition
;
1779 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1780 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1781 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1782 // checking can be done.
1784 while (Count
++ < 2) {
1785 NewPosition
= NewPosition
->BackLink
;
1789 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1790 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1791 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1792 // checking can be done.
1794 while (Count
-- > 0) {
1795 NewPosition
= NewPosition
->ForwardLink
;
1799 *CurrentPosition
= NewPosition
;
1802 return PadLineNumber
;
1806 Find HII Handle in the HII database associated with given Device Path.
1808 If DevicePath is NULL, then ASSERT.
1810 @param DevicePath Device Path associated with the HII package list
1813 @retval Handle HII package list Handle associated with the Device
1815 @retval NULL Hii Package list handle is not found.
1820 DevicePathToHiiHandle (
1821 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1825 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1830 EFI_HANDLE DriverHandle
;
1831 EFI_HII_HANDLE
*HiiHandles
;
1832 EFI_HII_HANDLE HiiHandle
;
1834 ASSERT (DevicePath
!= NULL
);
1836 TmpDevicePath
= DevicePath
;
1838 // Locate Device Path Protocol handle buffer
1840 Status
= gBS
->LocateDevicePath (
1841 &gEfiDevicePathProtocolGuid
,
1845 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1850 // Retrieve all HII Handles from HII database
1852 BufferSize
= 0x1000;
1853 HiiHandles
= AllocatePool (BufferSize
);
1854 ASSERT (HiiHandles
!= NULL
);
1855 Status
= mHiiDatabase
->ListPackageLists (
1857 EFI_HII_PACKAGE_TYPE_ALL
,
1862 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1863 FreePool (HiiHandles
);
1864 HiiHandles
= AllocatePool (BufferSize
);
1865 ASSERT (HiiHandles
!= NULL
);
1867 Status
= mHiiDatabase
->ListPackageLists (
1869 EFI_HII_PACKAGE_TYPE_ALL
,
1876 if (EFI_ERROR (Status
)) {
1877 FreePool (HiiHandles
);
1882 // Search Hii Handle by Driver Handle
1885 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1886 for (Index
= 0; Index
< HandleCount
; Index
++) {
1887 Status
= mHiiDatabase
->GetPackageListHandle (
1892 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1893 HiiHandle
= HiiHandles
[Index
];
1898 FreePool (HiiHandles
);
1903 Find HII Handle in the HII database associated with given form set guid.
1905 If FormSetGuid is NULL, then ASSERT.
1907 @param ComparingGuid FormSet Guid associated with the HII package list
1910 @retval Handle HII package list Handle associated with the Device
1912 @retval NULL Hii Package list handle is not found.
1916 FormSetGuidToHiiHandle (
1917 EFI_GUID
*ComparingGuid
1920 EFI_HII_HANDLE
*HiiHandles
;
1922 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1926 UINT32 PackageListLength
;
1927 EFI_HII_PACKAGE_HEADER PackageHeader
;
1931 EFI_HII_HANDLE HiiHandle
;
1933 ASSERT (ComparingGuid
!= NULL
);
1937 // Get all the Hii handles
1939 HiiHandles
= HiiGetHiiHandles (NULL
);
1940 ASSERT (HiiHandles
!= NULL
);
1943 // Search for formset of each class type
1945 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1947 HiiPackageList
= NULL
;
1948 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1949 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1950 HiiPackageList
= AllocatePool (BufferSize
);
1951 ASSERT (HiiPackageList
!= NULL
);
1953 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1955 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1960 // Get Form package from this HII package List
1962 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1964 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1966 while (Offset
< PackageListLength
) {
1967 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1968 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1970 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1972 // Search FormSet in this Form Package
1974 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1975 while (Offset2
< PackageHeader
.Length
) {
1976 OpCodeData
= Package
+ Offset2
;
1978 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1980 // Try to compare against formset GUID
1982 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1983 HiiHandle
= HiiHandles
[Index
];
1988 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1991 if (HiiHandle
!= NULL
) {
1994 Offset
+= PackageHeader
.Length
;
1997 FreePool (HiiPackageList
);
1998 if (HiiHandle
!= NULL
) {
2003 FreePool (HiiHandles
);
2009 Process the goto op code, update the info in the selection structure.
2011 @param Statement The statement belong to goto op code.
2012 @param Selection The selection info.
2013 @param Repaint Whether need to repaint the menu.
2014 @param NewLine Whether need to create new line.
2016 @retval EFI_SUCCESS The menu process successfully.
2017 @return Other value if the process failed.
2021 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
2022 IN OUT UI_MENU_SELECTION
*Selection
,
2023 OUT BOOLEAN
*Repaint
,
2024 OUT BOOLEAN
*NewLine
2028 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
2029 FORM_BROWSER_FORM
*RefForm
;
2033 Status
= EFI_SUCCESS
;
2037 // Prepare the device path check, get the device path info first.
2039 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
2040 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
2044 // Check whether the device path string is a valid string.
2046 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0 && StringPtr
!= NULL
) {
2047 if (Selection
->Form
->ModalForm
) {
2052 // Goto another Hii Package list
2054 if (mPathFromText
!= NULL
) {
2055 DevicePath
= mPathFromText
->ConvertTextToDevicePath(StringPtr
);
2056 if (DevicePath
!= NULL
) {
2057 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2058 FreePool (DevicePath
);
2060 FreePool (StringPtr
);
2063 // Not found the EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol.
2066 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gProtocolNotFound
, gPressEnter
, gEmptyString
);
2067 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2068 if (Repaint
!= NULL
) {
2071 FreePool (StringPtr
);
2075 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2076 if (Selection
->Handle
== NULL
) {
2078 // If target Hii Handle not found, exit
2080 Selection
->Action
= UI_ACTION_EXIT
;
2081 Selection
->Statement
= NULL
;
2085 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2086 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2087 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2088 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
2089 if (Selection
->Form
->ModalForm
) {
2093 // Goto another Formset, check for uncommitted data
2095 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2097 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
2098 if (Selection
->Handle
== NULL
) {
2100 // If target Hii Handle not found, exit
2102 Selection
->Action
= UI_ACTION_EXIT
;
2103 Selection
->Statement
= NULL
;
2107 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2108 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2109 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2110 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
2112 // Check whether target From is suppressed.
2114 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
2116 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2117 if (EvaluateExpressionList(RefForm
->SuppressExpression
, TRUE
, Selection
->FormSet
, RefForm
) != ExpressFalse
) {
2119 // Form is suppressed.
2122 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2123 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2124 if (Repaint
!= NULL
) {
2132 // Goto another form inside this formset,
2134 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2136 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2137 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2138 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
2140 // Goto another Question
2142 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2144 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2145 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2147 if (Repaint
!= NULL
) {
2150 if (NewLine
!= NULL
) {
2155 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2156 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2164 Display menu and wait for user to select one menu option, then return it.
2165 If AutoBoot is enabled, then if user doesn't select any option,
2166 after period of time, it will automatically return the first menu option.
2168 @param Selection Menu selection.
2170 @retval EFI_SUCESSS This function always return successfully for now.
2175 IN OUT UI_MENU_SELECTION
*Selection
2180 UINTN DistanceValue
;
2191 CHAR16
*OptionString
;
2192 CHAR16
*OutputString
;
2194 CHAR16
*HelpHeaderString
;
2195 CHAR16
*HelpBottomString
;
2201 BOOLEAN InitializedFlag
;
2206 LIST_ENTRY
*TopOfScreen
;
2207 LIST_ENTRY
*SavedListEntry
;
2208 UI_MENU_OPTION
*MenuOption
;
2209 UI_MENU_OPTION
*NextMenuOption
;
2210 UI_MENU_OPTION
*SavedMenuOption
;
2211 UI_MENU_OPTION
*PreviousMenuOption
;
2212 UI_CONTROL_FLAG ControlFlag
;
2213 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2214 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2215 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2216 UI_SCREEN_OPERATION ScreenOperation
;
2217 UINT8 MinRefreshInterval
;
2219 FORM_BROWSER_STATEMENT
*Statement
;
2220 UI_MENU_LIST
*CurrentMenu
;
2221 UINTN ModalSkipColumn
;
2222 BROWSER_HOT_KEY
*HotKey
;
2223 UINTN HelpPageIndex
;
2224 UINTN HelpPageCount
;
2227 UINTN HelpHeaderLine
;
2228 UINTN HelpBottomLine
;
2229 BOOLEAN MultiHelpPage
;
2231 UINT16 EachLineWidth
;
2232 UINT16 HeaderLineWidth
;
2233 UINT16 BottomLineWidth
;
2235 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2237 Status
= EFI_SUCCESS
;
2239 HelpHeaderString
= NULL
;
2240 HelpBottomString
= NULL
;
2241 OptionString
= NULL
;
2242 ScreenOperation
= UiNoOperation
;
2244 MinRefreshInterval
= 0;
2252 MultiHelpPage
= FALSE
;
2254 HeaderLineWidth
= 0;
2255 BottomLineWidth
= 0;
2256 OutputString
= NULL
;
2260 MenuRefreshEntry
= gMenuRefreshHead
;
2262 NextMenuOption
= NULL
;
2263 PreviousMenuOption
= NULL
;
2264 SavedMenuOption
= NULL
;
2266 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2268 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2270 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2271 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2272 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2274 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2275 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2278 if (Selection
->Form
->ModalForm
) {
2279 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2281 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2284 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2286 Selection
->TopRow
= TopRow
;
2287 Selection
->BottomRow
= BottomRow
;
2288 Selection
->PromptCol
= Col
;
2289 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2290 Selection
->Statement
= NULL
;
2292 TopOfScreen
= gMenuOption
.ForwardLink
;
2297 // Find current Menu
2299 CurrentMenu
= UiFindMenuList (Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2300 if (CurrentMenu
== NULL
) {
2302 // Current menu not found, add it to the menu tree
2304 CurrentMenu
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2306 ASSERT (CurrentMenu
!= NULL
);
2307 Selection
->CurrentMenu
= CurrentMenu
;
2309 if (Selection
->QuestionId
== 0) {
2311 // Highlight not specified, fetch it from cached menu
2313 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2314 Selection
->Sequence
= CurrentMenu
->Sequence
;
2318 // Init option as the current user's selection
2320 InitializedFlag
= TRUE
;
2321 NewPos
= gMenuOption
.ForwardLink
;
2323 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2324 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2326 ControlFlag
= CfInitialization
;
2327 Selection
->Action
= UI_ACTION_NONE
;
2329 switch (ControlFlag
) {
2330 case CfInitialization
:
2331 if (IsListEmpty (&gMenuOption
)) {
2332 ControlFlag
= CfReadKey
;
2334 ControlFlag
= CfCheckSelection
;
2338 case CfCheckSelection
:
2339 if (Selection
->Action
!= UI_ACTION_NONE
) {
2340 ControlFlag
= CfExit
;
2342 ControlFlag
= CfRepaint
;
2347 ControlFlag
= CfRefreshHighLight
;
2357 Temp
= (UINTN
) SkipValue
;
2358 Temp2
= (UINTN
) SkipValue
;
2361 // 1. Clear the screen.
2363 if (Selection
->Form
->ModalForm
) {
2365 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2366 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2367 TopRow
- SCROLL_ARROW_HEIGHT
,
2368 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2369 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2373 LocalScreen
.LeftColumn
,
2374 LocalScreen
.RightColumn
,
2375 TopRow
- SCROLL_ARROW_HEIGHT
,
2376 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2377 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2380 UiFreeRefreshList ();
2381 MinRefreshInterval
= 0;
2384 // 2.Paint the menu.
2386 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2387 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2388 MenuOption
->Row
= Row
;
2389 MenuOption
->Col
= Col
;
2390 if (Selection
->Form
->ModalForm
) {
2391 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2393 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2396 Statement
= MenuOption
->ThisTag
;
2397 if (Statement
->InSubtitle
) {
2398 MenuOption
->Col
+= SUBTITLE_INDENT
;
2401 if (MenuOption
->GrayOut
) {
2402 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2404 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2405 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2409 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2413 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2415 // Print Arrow for Goto button.
2418 MenuOption
->Col
- 2,
2421 GEOMETRICSHAPE_RIGHT_TRIANGLE
2426 // 2.1. Paint the description.
2428 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2430 // Temp means need to skip how many lines from the start.
2432 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2433 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2436 // If there is more string to process print on the next row and increment the Skip value
2438 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2444 FreePool (OutputString
);
2454 // 2.2. Paint the option string.
2456 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2457 if (EFI_ERROR (Status
)) {
2459 // Repaint to clear possible error prompt pop-up
2463 ControlFlag
= CfRepaint
;
2467 if (OptionString
!= NULL
) {
2468 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2469 ProcessStringForDateTime(MenuOption
, OptionString
, TRUE
);
2472 Width
= (UINT16
) gOptionBlockWidth
;
2476 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2477 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2478 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2481 // If there is more string to process print on the next row and increment the Skip value
2483 if (StrLen (&OptionString
[Index
]) != 0) {
2487 // Since the Number of lines for this menu entry may or may not be reflected accurately
2488 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2489 // some testing to ensure we are keeping this in-sync.
2491 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2493 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2499 FreePool (OutputString
);
2508 FreePool (OptionString
);
2512 // 2.4 Special process for Test opcode with test two.
2514 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2515 if (gMenuEventGuidRefreshHead
== NULL
) {
2516 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2517 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2519 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2520 while (MenuUpdateEntry
->Next
!= NULL
) {
2521 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2523 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2524 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2526 ASSERT (MenuUpdateEntry
!= NULL
);
2527 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2528 ASSERT (!EFI_ERROR (Status
));
2529 MenuUpdateEntry
->MenuOption
= MenuOption
;
2530 MenuUpdateEntry
->Selection
= Selection
;
2531 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2532 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2533 if (MenuOption
->GrayOut
) {
2534 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2536 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2541 // If Question request refresh, register the op-code
2543 if (Statement
->RefreshInterval
!= 0) {
2545 // Menu will be refreshed at minimal interval of all Questions
2546 // which have refresh request
2548 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2549 MinRefreshInterval
= Statement
->RefreshInterval
;
2552 if (gMenuRefreshHead
== NULL
) {
2553 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2554 gMenuRefreshHead
= MenuRefreshEntry
;
2556 MenuRefreshEntry
= gMenuRefreshHead
;
2557 while (MenuRefreshEntry
->Next
!= NULL
) {
2558 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2560 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2561 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2563 ASSERT (MenuRefreshEntry
!= NULL
);
2564 MenuRefreshEntry
->MenuOption
= MenuOption
;
2565 MenuRefreshEntry
->Selection
= Selection
;
2566 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2567 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2568 if (MenuOption
->GrayOut
) {
2569 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2571 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2576 // If this is a text op with secondary text information
2578 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2579 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2581 Width
= (UINT16
) gOptionBlockWidth
;
2585 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2586 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2587 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2590 // If there is more string to process print on the next row and increment the Skip value
2592 if (StrLen (&StringPtr
[Index
]) != 0) {
2596 // Since the Number of lines for this menu entry may or may not be reflected accurately
2597 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2598 // some testing to ensure we are keeping this in-sync.
2600 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2602 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2608 FreePool (OutputString
);
2615 FreePool (StringPtr
);
2617 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2620 // 3. Update the row info which will be used by next menu.
2622 if (Link
== TopOfScreen
) {
2623 Row
+= MenuOption
->Skip
- SkipValue
;
2625 Row
+= MenuOption
->Skip
;
2628 if (Row
> BottomRow
) {
2629 if (!ValueIsScroll (FALSE
, Link
)) {
2633 Row
= BottomRow
+ 1;
2638 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2643 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2645 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2646 TopRow
- SCROLL_ARROW_HEIGHT
,
2650 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2654 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2656 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2657 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2661 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2668 case CfRefreshHighLight
:
2670 // MenuOption: Last menu option that need to remove hilight
2671 // MenuOption is set to NULL in Repaint
2672 // NewPos: Current menu option that need to hilight
2674 ControlFlag
= CfUpdateHelpString
;
2675 if (TopOfScreen
== &MenuOption
->Link
) {
2680 if (NewPos
== TopOfScreen
) {
2685 if (InitializedFlag
) {
2686 InitializedFlag
= FALSE
;
2687 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2691 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2692 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2694 SavedValue
= Repaint
;
2697 if (Selection
->QuestionId
!= 0) {
2698 NewPos
= gMenuOption
.ForwardLink
;
2699 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2701 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2702 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2703 NewPos
->ForwardLink
!= &gMenuOption
) {
2704 NewPos
= NewPos
->ForwardLink
;
2705 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2707 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2709 // Target Question found, find its MenuOption
2713 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2714 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2715 Index
+= SavedMenuOption
->Skip
;
2716 if (Link
== TopOfScreen
) {
2719 Link
= Link
->ForwardLink
;
2721 if (NewPos
== Link
) {
2722 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2726 // Not find the selected menu in current show page.
2727 // Have two case to enter this if:
2728 // 1. Not find the menu at current page.
2729 // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
2730 // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
2732 // Base on the selected menu will show at the bottom of the page,
2733 // select the menu which will show at the top of the page.
2735 if (Link
!= NewPos
|| Index
> BottomRow
||
2736 (Link
== NewPos
&& (SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
) && (Link
!= TopOfScreen
))) {
2738 // Find the MenuOption which has the skip value for Date/Time opcode.
2740 AdjustDateAndTimePosition(FALSE
, &NewPos
);
2742 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2744 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2746 // SavedMenuOption->Row == 0 means the menu not show yet.
2748 if (SavedMenuOption
->Row
== 0) {
2749 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2753 // Base on the selected menu will show at the bottome of next page,
2754 // select the menu show at the top of the next page.
2757 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2758 Link
= Link
->BackLink
;
2759 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2760 if (SavedMenuOption
->Row
== 0) {
2761 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2763 Index
+= SavedMenuOption
->Skip
;
2767 // Found the menu which will show at the top of the page.
2769 if (Link
== NewPos
) {
2771 // The menu can show more than one pages, just show the menu at the top of the page.
2777 // Check whether need to skip some line for menu shows at the top of the page.
2779 SkipValue
= Index
- BottomRow
- 1;
2780 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2784 TopOfScreen
= Link
->ForwardLink
;
2790 ControlFlag
= CfRepaint
;
2795 // Target Question not found, highlight the default menu option
2797 NewPos
= TopOfScreen
;
2800 Selection
->QuestionId
= 0;
2803 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2804 if (MenuOption
!= NULL
) {
2806 // Remove highlight on last Menu Option
2808 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2809 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2810 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2811 if (OptionString
!= NULL
) {
2812 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2813 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2815 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2818 Width
= (UINT16
) gOptionBlockWidth
;
2819 OriginalRow
= MenuOption
->Row
;
2822 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2823 if ((Temp
== 0) && (MenuOption
->Row
>= TopRow
) && (MenuOption
->Row
<= BottomRow
)) {
2824 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2827 // If there is more string to process print on the next row and increment the Skip value
2829 if (StrLen (&OptionString
[Index
]) != 0) {
2835 FreePool (OutputString
);
2841 MenuOption
->Row
= OriginalRow
;
2843 FreePool (OptionString
);
2846 if (MenuOption
->GrayOut
) {
2847 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2848 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2849 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2852 OriginalRow
= MenuOption
->Row
;
2853 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2856 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2857 if ((Temp
== 0) && (MenuOption
->Row
>= TopRow
) && (MenuOption
->Row
<= BottomRow
)) {
2858 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2861 // If there is more string to process print on the next row and increment the Skip value
2863 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2869 FreePool (OutputString
);
2875 MenuOption
->Row
= OriginalRow
;
2876 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2882 // This is the current selected statement
2884 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2885 Statement
= MenuOption
->ThisTag
;
2886 Selection
->Statement
= Statement
;
2887 if (!IsSelectable (MenuOption
)) {
2888 Repaint
= SavedValue
;
2889 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2894 // Record highlight for current menu
2896 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2897 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2900 // Set reverse attribute
2902 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2903 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2906 // Assuming that we have a refresh linked-list created, lets annotate the
2907 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2908 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2910 if (gMenuRefreshHead
!= NULL
) {
2911 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2912 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2913 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2915 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2917 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2918 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2923 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2924 if (OptionString
!= NULL
) {
2925 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2926 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2928 Width
= (UINT16
) gOptionBlockWidth
;
2930 OriginalRow
= MenuOption
->Row
;
2933 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2934 if ((Temp2
== 0) && (MenuOption
->Row
>= TopRow
) && (MenuOption
->Row
<= BottomRow
) ) {
2935 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2938 // If there is more string to process print on the next row and increment the Skip value
2940 if (StrLen (&OptionString
[Index
]) != 0) {
2946 FreePool (OutputString
);
2952 MenuOption
->Row
= OriginalRow
;
2954 FreePool (OptionString
);
2957 OriginalRow
= MenuOption
->Row
;
2959 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2962 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2963 if ((Temp2
== 0) && (MenuOption
->Row
>= TopRow
) && (MenuOption
->Row
<= BottomRow
) ) {
2964 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2967 // If there is more string to process print on the next row and increment the Skip value
2969 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2975 FreePool (OutputString
);
2981 MenuOption
->Row
= OriginalRow
;
2986 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2989 // Clear reverse attribute
2991 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2994 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2995 // if we didn't break halfway when process CfRefreshHighLight.
2997 Repaint
= SavedValue
;
3000 case CfUpdateHelpString
:
3001 ControlFlag
= CfPrepareToReadKey
;
3002 if (Selection
->Form
->ModalForm
) {
3006 if (Repaint
|| NewLine
) {
3008 // Don't print anything if it is a NULL help token
3010 ASSERT(MenuOption
!= NULL
);
3011 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
3014 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
3017 RowCount
= BottomRow
- TopRow
;
3020 // 1.Calculate how many line the help string need to print.
3022 if (HelpString
!= NULL
) {
3023 FreePool (HelpString
);
3025 HelpLine
= ProcessHelpString (StringPtr
, &HelpString
, &EachLineWidth
, RowCount
);
3026 if (HelpLine
> RowCount
) {
3027 MultiHelpPage
= TRUE
;
3028 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP
), gHiiHandle
);
3029 if (HelpHeaderString
!= NULL
) {
3030 FreePool (HelpHeaderString
);
3032 HelpHeaderLine
= ProcessHelpString (StringPtr
, &HelpHeaderString
, &HeaderLineWidth
, RowCount
);
3033 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN
), gHiiHandle
);
3034 if (HelpBottomString
!= NULL
) {
3035 FreePool (HelpBottomString
);
3037 HelpBottomLine
= ProcessHelpString (StringPtr
, &HelpBottomString
, &BottomLineWidth
, RowCount
);
3039 // Calculate the help page count.
3041 if (HelpLine
> 2 * RowCount
- 2) {
3042 HelpPageCount
= (HelpLine
- RowCount
+ 1) / (RowCount
- 2) + 1;
3043 if ((HelpLine
- RowCount
+ 1) % (RowCount
- 2) > 1) {
3050 MultiHelpPage
= FALSE
;
3055 // Clean the help field first.
3058 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3059 LocalScreen
.RightColumn
,
3062 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
3066 // Check whether need to show the 'More(U/u)' at the begin.
3067 // Base on current direct info, here shows aligned to the right side of the column.
3068 // If the direction is multi line and aligned to right side may have problem, so
3069 // add ASSERT code here.
3071 if (HelpPageIndex
> 0) {
3072 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3073 for (Index
= 0; Index
< HelpHeaderLine
; Index
++) {
3074 ASSERT (HelpHeaderLine
== 1);
3075 ASSERT (GetStringWidth (HelpHeaderString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3077 LocalScreen
.RightColumn
- GetStringWidth (HelpHeaderString
) / 2 - 1,
3079 &HelpHeaderString
[Index
* HeaderLineWidth
]
3084 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
3086 // Print the help string info.
3088 if (!MultiHelpPage
) {
3089 for (Index
= 0; Index
< HelpLine
; Index
++) {
3091 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3093 &HelpString
[Index
* EachLineWidth
]
3096 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3098 if (HelpPageIndex
== 0) {
3099 for (Index
= 0; Index
< RowCount
- HelpBottomLine
; Index
++) {
3101 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3103 &HelpString
[Index
* EachLineWidth
]
3107 for (Index
= 0; (Index
< RowCount
- HelpBottomLine
- HelpHeaderLine
) &&
3108 (Index
+ HelpPageIndex
* (RowCount
- 2) + 1 < HelpLine
); Index
++) {
3110 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3111 Index
+ TopRow
+ HelpHeaderLine
,
3112 &HelpString
[(Index
+ HelpPageIndex
* (RowCount
- 2) + 1)* EachLineWidth
]
3115 if (HelpPageIndex
== HelpPageCount
- 1) {
3116 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3122 // Check whether need to print the 'More(D/d)' at the bottom.
3123 // Base on current direct info, here shows aligned to the right side of the column.
3124 // If the direction is multi line and aligned to right side may have problem, so
3125 // add ASSERT code here.
3127 if (HelpPageIndex
< HelpPageCount
- 1 && MultiHelpPage
) {
3128 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3129 for (Index
= 0; Index
< HelpBottomLine
; Index
++) {
3130 ASSERT (HelpBottomLine
== 1);
3131 ASSERT (GetStringWidth (HelpBottomString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3133 LocalScreen
.RightColumn
- GetStringWidth (HelpBottomString
) / 2 - 1,
3134 Index
+ BottomRow
- HelpBottomLine
,
3135 &HelpBottomString
[Index
* BottomLineWidth
]
3140 // Reset this flag every time we finish using it.
3146 case CfPrepareToReadKey
:
3147 ControlFlag
= CfReadKey
;
3148 ScreenOperation
= UiNoOperation
;
3152 ControlFlag
= CfScreenOperation
;
3155 // Wait for user's selection
3158 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
3159 if (!EFI_ERROR (Status
)) {
3164 // If we encounter error, continue to read another key in.
3166 if (Status
!= EFI_NOT_READY
) {
3170 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
3171 ASSERT_EFI_ERROR (Status
);
3173 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
3175 // IFR is updated in Callback of refresh opcode, re-parse it
3177 ControlFlag
= CfCheckSelection
;
3178 Selection
->Statement
= NULL
;
3183 if (ControlFlag
== CfCheckSelection
) {
3187 switch (Key
.UnicodeChar
) {
3188 case CHAR_CARRIAGE_RETURN
:
3189 if(MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3190 ControlFlag
= CfReadKey
;
3194 ScreenOperation
= UiSelect
;
3199 // We will push the adjustment of these numeric values directly to the input handler
3200 // NOTE: we won't handle manual input numeric
3205 // If the screen has no menu items, and the user didn't select UiReset
3206 // ignore the selection and go back to reading keys.
3208 if(IsListEmpty (&gMenuOption
) || MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3209 ControlFlag
= CfReadKey
;
3213 ASSERT(MenuOption
!= NULL
);
3214 Statement
= MenuOption
->ThisTag
;
3215 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
3216 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
3217 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
3219 if (Key
.UnicodeChar
== '+') {
3220 gDirection
= SCAN_RIGHT
;
3222 gDirection
= SCAN_LEFT
;
3224 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3225 if (EFI_ERROR (Status
)) {
3227 // Repaint to clear possible error prompt pop-up
3232 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3234 if (OptionString
!= NULL
) {
3235 FreePool (OptionString
);
3241 ScreenOperation
= UiUp
;
3246 ScreenOperation
= UiDown
;
3250 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
3252 // If the screen has no menu items, and the user didn't select UiReset
3253 // ignore the selection and go back to reading keys.
3255 if(IsListEmpty (&gMenuOption
)) {
3256 ControlFlag
= CfReadKey
;
3260 ASSERT(MenuOption
!= NULL
);
3261 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
&& !MenuOption
->ReadOnly
) {
3262 ScreenOperation
= UiSelect
;
3269 if (!MultiHelpPage
) {
3270 ControlFlag
= CfReadKey
;
3273 ControlFlag
= CfUpdateHelpString
;
3274 HelpPageIndex
= HelpPageIndex
< HelpPageCount
- 1 ? HelpPageIndex
+ 1 : HelpPageCount
- 1;
3279 if (!MultiHelpPage
) {
3280 ControlFlag
= CfReadKey
;
3283 ControlFlag
= CfUpdateHelpString
;
3284 HelpPageIndex
= HelpPageIndex
> 0 ? HelpPageIndex
- 1 : 0;
3288 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
3289 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
3290 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
3295 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
3297 // ModalForm has no ESC key and Hot Key.
3299 ControlFlag
= CfReadKey
;
3300 } else if (Index
== mScanCodeNumber
) {
3302 // Check whether Key matches the registered hot key.
3305 if ((gBrowserSettingScope
== SystemLevel
) ||
3306 (Selection
->FormEditable
&& gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
3307 HotKey
= GetHotKeyFromRegisterList (&Key
);
3309 if (HotKey
!= NULL
) {
3310 ScreenOperation
= UiHotKey
;
3317 case CfScreenOperation
:
3318 if (ScreenOperation
!= UiReset
) {
3320 // If the screen has no menu items, and the user didn't select UiReset
3321 // ignore the selection and go back to reading keys.
3323 if (IsListEmpty (&gMenuOption
)) {
3324 ControlFlag
= CfReadKey
;
3330 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
3333 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
3334 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
3341 ControlFlag
= CfCheckSelection
;
3343 ASSERT(MenuOption
!= NULL
);
3344 Statement
= MenuOption
->ThisTag
;
3345 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
3350 // Keep highlight on current MenuOption
3352 Selection
->QuestionId
= Statement
->QuestionId
;
3354 switch (Statement
->Operand
) {
3355 case EFI_IFR_REF_OP
:
3356 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
3359 case EFI_IFR_ACTION_OP
:
3361 // Process the Config string <ConfigResp>
3363 Status
= ProcessQuestionConfig (Selection
, Statement
);
3365 if (EFI_ERROR (Status
)) {
3370 // The action button may change some Question value, so refresh the form
3372 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3375 case EFI_IFR_RESET_BUTTON_OP
:
3377 // Reset Question to default value specified by DefaultId
3379 ControlFlag
= CfUiDefault
;
3380 DefaultId
= Statement
->DefaultId
;
3385 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3387 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
3388 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3390 if (EFI_ERROR (Status
)) {
3393 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
3395 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3398 if (OptionString
!= NULL
) {
3399 FreePool (OptionString
);
3407 // We come here when someone press ESC
3409 ControlFlag
= CfCheckSelection
;
3410 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3414 ControlFlag
= CfCheckSelection
;
3415 ASSERT(MenuOption
!= NULL
);
3416 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3417 if (MenuOption
->Sequence
!= 0) {
3419 // In the middle or tail of the Date/Time op-code set, go left.
3421 ASSERT(NewPos
!= NULL
);
3422 NewPos
= NewPos
->BackLink
;
3428 ControlFlag
= CfCheckSelection
;
3429 ASSERT(MenuOption
!= NULL
);
3430 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3431 if (MenuOption
->Sequence
!= 2) {
3433 // In the middle or tail of the Date/Time op-code set, go left.
3435 ASSERT(NewPos
!= NULL
);
3436 NewPos
= NewPos
->ForwardLink
;
3442 ControlFlag
= CfCheckSelection
;
3444 SavedListEntry
= NewPos
;
3446 ASSERT(NewPos
!= NULL
);
3448 // Adjust Date/Time position before we advance forward.
3450 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3451 if (NewPos
->BackLink
!= &gMenuOption
) {
3452 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3453 ASSERT (MenuOption
!= NULL
);
3455 NewPos
= NewPos
->BackLink
;
3457 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3458 if (PreviousMenuOption
->Row
== 0) {
3459 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3461 DistanceValue
= PreviousMenuOption
->Skip
;
3463 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3464 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3466 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3468 if (Difference
< 0) {
3470 // We hit the begining MenuOption that can be focused
3471 // so we simply scroll to the top.
3473 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3474 TopOfScreen
= gMenuOption
.ForwardLink
;
3478 // Scroll up to the last page when we have arrived at top page.
3480 NewPos
= &gMenuOption
;
3481 TopOfScreen
= &gMenuOption
;
3482 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3483 ScreenOperation
= UiPageUp
;
3484 ControlFlag
= CfScreenOperation
;
3487 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3489 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3491 TopOfScreen
= NewPos
;
3494 } else if (!IsSelectable (NextMenuOption
)) {
3496 // Continue to go up until scroll to next page or the selectable option is found.
3498 ScreenOperation
= UiUp
;
3499 ControlFlag
= CfScreenOperation
;
3503 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3505 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3506 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3507 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3508 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3511 // Scroll up to the last page.
3513 NewPos
= &gMenuOption
;
3514 TopOfScreen
= &gMenuOption
;
3515 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3516 ScreenOperation
= UiPageUp
;
3517 ControlFlag
= CfScreenOperation
;
3523 // SkipValue means lines is skipped when show the top menu option.
3525 ControlFlag
= CfCheckSelection
;
3527 ASSERT(NewPos
!= NULL
);
3529 // Already at the first menu option, so do nothing.
3531 if (NewPos
->BackLink
== &gMenuOption
) {
3541 // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
3542 // form of options to be show, so just update the SkipValue to show the next
3543 // parts of options.
3545 if (SkipValue
> (INTN
) (BottomRow
- TopRow
+ 1)) {
3546 SkipValue
-= BottomRow
- TopRow
+ 1;
3552 // First minus the menu of the top screen, it's value is SkipValue.
3554 Index
= (BottomRow
+ 1) - SkipValue
;
3555 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3556 Link
= Link
->BackLink
;
3557 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3558 if (PreviousMenuOption
->Row
== 0) {
3559 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3561 if (Index
< PreviousMenuOption
->Skip
) {
3564 Index
= Index
- PreviousMenuOption
->Skip
;
3567 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3569 if (TopOfScreen
== &gMenuOption
) {
3570 TopOfScreen
= gMenuOption
.ForwardLink
;
3571 NewPos
= gMenuOption
.BackLink
;
3572 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3574 } else if (TopOfScreen
!= Link
) {
3577 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3580 // Finally we know that NewPos is the last MenuOption can be focused.
3584 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3587 if (Index
>= TopRow
) {
3589 // At here, only case "Index < PreviousMenuOption->Skip" can reach here.
3591 SkipValue
= PreviousMenuOption
->Skip
- (Index
- TopRow
);
3593 SkipValue
= PreviousMenuOption
->Skip
- (TopRow
- Index
);
3594 Link
= Link
->ForwardLink
;
3598 // Move to the option in Next page.
3600 if (TopOfScreen
== &gMenuOption
) {
3601 NewPos
= gMenuOption
.BackLink
;
3602 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3605 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3609 // There are more MenuOption needing scrolling up.
3616 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3617 // Don't do this when we are already in the first page.
3619 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3620 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3625 // SkipValue means lines is skipped when show the top menu option.
3627 ControlFlag
= CfCheckSelection
;
3629 ASSERT (NewPos
!= NULL
);
3630 if (NewPos
->ForwardLink
== &gMenuOption
) {
3639 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3640 Index
= TopRow
+ NextMenuOption
->Skip
- SkipValue
;
3642 // Count to the menu option which will show at the top of the next form.
3644 while ((Index
<= BottomRow
+ 1) && (Link
->ForwardLink
!= &gMenuOption
)) {
3645 Link
= Link
->ForwardLink
;
3646 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3647 Index
= Index
+ NextMenuOption
->Skip
;
3650 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
+ 1)) {
3652 // Finally we know that NewPos is the last MenuOption can be focused.
3655 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3659 // Calculate the skip line for top of screen menu.
3661 if (Link
== TopOfScreen
) {
3663 // The top of screen menu option occupies the entire form.
3665 SkipValue
+= BottomRow
- TopRow
+ 1;
3667 SkipValue
= NextMenuOption
->Skip
- (Index
- (BottomRow
+ 1));
3673 // Move to the Next selectable menu.
3675 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3679 // Save the menu as the next highlight menu.
3684 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3685 // Don't do this when we are already in the last page.
3687 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3688 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3693 // SkipValue means lines is skipped when show the top menu option.
3694 // NewPos points to the menu which is highlighted now.
3696 ControlFlag
= CfCheckSelection
;
3698 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3699 // to be one that progresses to the next set of op-codes, we need to advance to the last
3700 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3701 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3702 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3703 // the Date/Time op-code.
3705 SavedListEntry
= NewPos
;
3706 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3708 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3709 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3711 NewPos
= NewPos
->ForwardLink
;
3715 // Current menu not at the bottom of the form.
3717 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3719 // Find the next selectable menu.
3721 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3723 // We hit the end of MenuOption that can be focused
3724 // so we simply scroll to the first page.
3726 if (Difference
< 0) {
3728 // Scroll to the first page.
3730 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3731 TopOfScreen
= gMenuOption
.ForwardLink
;
3735 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3737 NewPos
= gMenuOption
.ForwardLink
;
3738 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3742 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3744 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3745 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3749 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3750 if (NextMenuOption
->Row
== 0) {
3751 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3753 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3755 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3756 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3757 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3758 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3764 // If we are going to scroll, update TopOfScreen
3766 if (Temp
> BottomRow
) {
3769 // Is the current top of screen a zero-advance op-code?
3770 // If so, keep moving forward till we hit a >0 advance op-code
3772 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3775 // If bottom op-code is more than one line or top op-code is more than one line
3777 if ((DistanceValue
> 1) || (SavedMenuOption
->Skip
> 1)) {
3779 // Is the bottom op-code greater than or equal in size to the top op-code?
3781 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- SkipValue
)) {
3783 // Skip the top op-code
3785 TopOfScreen
= TopOfScreen
->ForwardLink
;
3786 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- SkipValue
);
3788 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3791 // If we have a remainder, skip that many more op-codes until we drain the remainder
3793 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3795 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3797 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3798 TopOfScreen
= TopOfScreen
->ForwardLink
;
3799 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3802 // Since we will act on this op-code in the next routine, and increment the
3803 // SkipValue, set the skips to one less than what is required.
3805 SkipValue
= Difference
- 1;
3808 // Since we will act on this op-code in the next routine, and increment the
3809 // SkipValue, set the skips to one less than what is required.
3811 SkipValue
+= (Temp
- BottomRow
) - 1;
3814 if ((SkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3815 TopOfScreen
= TopOfScreen
->ForwardLink
;
3820 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3821 // Let's set a skip flag to smoothly scroll the top of the screen.
3823 if (SavedMenuOption
->Skip
> 1) {
3824 if (SavedMenuOption
== NextMenuOption
) {
3829 } else if (SavedMenuOption
->Skip
== 1) {
3833 TopOfScreen
= TopOfScreen
->ForwardLink
;
3835 } while (SavedMenuOption
->Skip
== 0);
3838 } else if (!IsSelectable (NextMenuOption
)) {
3840 // Continue to go down until scroll to next page or the selectable option is found.
3842 ScreenOperation
= UiDown
;
3843 ControlFlag
= CfScreenOperation
;
3846 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3848 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3852 // Scroll to the first page.
3854 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3855 TopOfScreen
= gMenuOption
.ForwardLink
;
3860 // Need to remove the current highlight menu.
3861 // MenuOption saved the last highlight menu info.
3863 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3869 // Get the next highlight menu.
3871 NewPos
= gMenuOption
.ForwardLink
;
3872 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3876 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3878 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3879 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3883 ControlFlag
= CfCheckSelection
;
3885 Status
= EFI_SUCCESS
;
3887 // Discard changes. After it, no NV flag is showed.
3889 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3890 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3891 if (!EFI_ERROR (Status
)) {
3892 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3893 Selection
->Statement
= NULL
;
3894 gResetRequired
= FALSE
;
3897 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3898 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3900 // Still show current page.
3902 Selection
->Action
= UI_ACTION_NONE
;
3910 // Reterieve default setting. After it. NV flag will be showed.
3912 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3913 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
, GetDefaultForAll
, NULL
, FALSE
);
3914 if (!EFI_ERROR (Status
)) {
3915 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3916 Selection
->Statement
= NULL
;
3917 gResetRequired
= TRUE
;
3920 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3921 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3923 // Still show current page.
3925 Selection
->Action
= UI_ACTION_NONE
;
3933 // Save changes. After it, no NV flag is showed.
3935 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3936 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3937 if (!EFI_ERROR (Status
)) {
3938 ASSERT(MenuOption
!= NULL
);
3939 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3940 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3943 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3944 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3946 // Still show current page.
3948 Selection
->Action
= UI_ACTION_NONE
;
3956 // Set Reset required Flag
3958 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3959 gResetRequired
= TRUE
;
3965 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3967 // Form Exit without saving, Similar to ESC Key.
3968 // FormSet Exit without saving, Exit SendForm.
3969 // System Exit without saving, CallExitHandler and Exit SendForm.
3971 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3972 if (gBrowserSettingScope
== FormLevel
) {
3973 ControlFlag
= CfUiReset
;
3974 } else if (gBrowserSettingScope
== FormSetLevel
) {
3975 Selection
->Action
= UI_ACTION_EXIT
;
3976 } else if (gBrowserSettingScope
== SystemLevel
) {
3977 if (ExitHandlerFunction
!= NULL
) {
3978 ExitHandlerFunction ();
3980 Selection
->Action
= UI_ACTION_EXIT
;
3982 Selection
->Statement
= NULL
;
3987 ControlFlag
= CfCheckSelection
;
3989 // Reset to default value for all forms in the whole system.
3991 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
, GetDefaultForAll
, NULL
, FALSE
);
3993 if (!EFI_ERROR (Status
)) {
3994 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3995 Selection
->Statement
= NULL
;
3996 gResetRequired
= TRUE
;
4000 case CfUiNoOperation
:
4001 ControlFlag
= CfCheckSelection
;
4005 UiFreeRefreshList ();
4007 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
4008 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
4009 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
4010 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");
4011 if (HelpString
!= NULL
) {
4012 FreePool (HelpString
);
4014 if (HelpHeaderString
!= NULL
) {
4015 FreePool (HelpHeaderString
);
4017 if (HelpBottomString
!= NULL
) {
4018 FreePool (HelpBottomString
);