2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2010, Intel Corporation
5 All rights reserved. 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
;
18 LIST_ENTRY gMenuList
= INITIALIZE_LIST_HEAD_VARIABLE (gMenuList
);
19 MENU_REFRESH_ENTRY
*gMenuRefreshHead
;
22 // Search table for UiDisplayMenu()
24 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
63 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
111 BOOLEAN GetLineByWidthFinished
= FALSE
;
115 Set Buffer to Value for Size bytes.
117 @param Buffer Memory to set.
118 @param Size Number of bytes to set
119 @param Value Value of the set operation.
132 while ((Size
--) != 0) {
139 Initialize Menu option list.
147 InitializeListHead (&gMenuOption
);
152 Free Menu option linked list.
160 UI_MENU_OPTION
*MenuOption
;
162 while (!IsListEmpty (&gMenuOption
)) {
163 MenuOption
= MENU_OPTION_FROM_LINK (gMenuOption
.ForwardLink
);
164 RemoveEntryList (&MenuOption
->Link
);
167 // We allocated space for this description when we did a GetToken, free it here
169 if (MenuOption
->Skip
!= 0) {
171 // For date/time, MenuOption->Description is shared by three Menu Options
172 // Data format : [01/02/2004] [11:22:33]
173 // Line number : 0 0 1 0 0 1
175 FreePool (MenuOption
->Description
);
177 FreePool (MenuOption
);
183 Create a menu with specified formset GUID and form ID, and add it as a child
184 of the given parent menu.
186 @param Parent The parent of menu to be added.
187 @param FormSetGuid The Formset Guid of menu to be added.
188 @param FormId The Form ID of menu to be added.
190 @return A pointer to the newly added menu or NULL if memory is insufficient.
195 IN OUT UI_MENU_LIST
*Parent
,
196 IN EFI_GUID
*FormSetGuid
,
200 UI_MENU_LIST
*MenuList
;
202 MenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
203 if (MenuList
== NULL
) {
207 MenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
208 InitializeListHead (&MenuList
->ChildListHead
);
210 CopyMem (&MenuList
->FormSetGuid
, FormSetGuid
, sizeof (EFI_GUID
));
211 MenuList
->FormId
= FormId
;
212 MenuList
->Parent
= Parent
;
214 if (Parent
== NULL
) {
216 // If parent is not specified, it is the root Form of a Formset
218 InsertTailList (&gMenuList
, &MenuList
->Link
);
220 InsertTailList (&Parent
->ChildListHead
, &MenuList
->Link
);
228 Search Menu with given FormId in the parent menu and all its child menus.
230 @param Parent The parent of menu to search.
231 @param FormId The Form ID of menu to search.
233 @return A pointer to menu found or NULL if not found.
237 UiFindChildMenuList (
238 IN UI_MENU_LIST
*Parent
,
244 UI_MENU_LIST
*MenuList
;
246 if (Parent
->FormId
== FormId
) {
250 Link
= GetFirstNode (&Parent
->ChildListHead
);
251 while (!IsNull (&Parent
->ChildListHead
, Link
)) {
252 Child
= UI_MENU_LIST_FROM_LINK (Link
);
254 MenuList
= UiFindChildMenuList (Child
, FormId
);
255 if (MenuList
!= NULL
) {
259 Link
= GetNextNode (&Parent
->ChildListHead
, Link
);
267 Search Menu with given FormSetGuid and FormId in all cached menu list.
269 @param FormSetGuid The Formset GUID of the menu to search.
270 @param FormId The Form ID of menu to search.
272 @return A pointer to menu found or NULL if not found.
277 IN EFI_GUID
*FormSetGuid
,
282 UI_MENU_LIST
*MenuList
;
285 Link
= GetFirstNode (&gMenuList
);
286 while (!IsNull (&gMenuList
, Link
)) {
287 MenuList
= UI_MENU_LIST_FROM_LINK (Link
);
289 if (CompareGuid (FormSetGuid
, &MenuList
->FormSetGuid
)) {
291 // This is the formset we are looking for, find the form in this formset
293 Child
= UiFindChildMenuList (MenuList
, FormId
);
299 Link
= GetNextNode (&gMenuList
, Link
);
307 Free Menu option linked list.
315 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
317 while (gMenuRefreshHead
!= NULL
) {
318 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
319 FreePool (gMenuRefreshHead
);
320 gMenuRefreshHead
= OldMenuRefreshEntry
;
323 gMenuRefreshHead
= NULL
;
337 CHAR16
*OptionString
;
338 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
341 UI_MENU_SELECTION
*Selection
;
342 FORM_BROWSER_STATEMENT
*Question
;
343 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
344 EFI_HII_VALUE
*HiiValue
;
345 EFI_BROWSER_ACTION_REQUEST ActionRequest
;
347 if (gMenuRefreshHead
!= NULL
) {
349 MenuRefreshEntry
= gMenuRefreshHead
;
352 // Reset FormPackage update flag
354 mHiiPackageListUpdated
= FALSE
;
357 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
359 Selection
= MenuRefreshEntry
->Selection
;
360 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
362 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
363 if (EFI_ERROR (Status
)) {
368 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
370 if (OptionString
!= NULL
) {
372 // If leading spaces on OptionString - remove the spaces
374 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
377 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, &OptionString
[Index
]);
378 FreePool (OptionString
);
382 // Question value may be changed, need invoke its Callback()
384 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
385 if (((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) && (ConfigAccess
!= NULL
)) {
386 ActionRequest
= EFI_BROWSER_ACTION_REQUEST_NONE
;
388 HiiValue
= &Question
->HiiValue
;
389 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
391 // Create String in HII database for Configuration Driver to retrieve
393 HiiValue
->Value
.string
= NewString ((CHAR16
*) Question
->BufferValue
, Selection
->FormSet
->HiiHandle
);
396 Status
= ConfigAccess
->Callback (
398 EFI_BROWSER_ACTION_CHANGING
,
399 Question
->QuestionId
,
405 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
407 // Clean the String in HII Database
409 DeleteString (HiiValue
->Value
.string
, Selection
->FormSet
->HiiHandle
);
412 if (!EFI_ERROR (Status
)) {
413 switch (ActionRequest
) {
414 case EFI_BROWSER_ACTION_REQUEST_RESET
:
415 gResetRequired
= TRUE
;
418 case EFI_BROWSER_ACTION_REQUEST_SUBMIT
:
419 SubmitForm (Selection
->FormSet
, Selection
->Form
);
422 case EFI_BROWSER_ACTION_REQUEST_EXIT
:
423 Selection
->Action
= UI_ACTION_EXIT
;
424 gNvUpdateRequired
= FALSE
;
433 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
435 } while (MenuRefreshEntry
!= NULL
);
437 if (mHiiPackageListUpdated
) {
439 // Package list is updated, force to reparse IFR binary of target Formset
441 mHiiPackageListUpdated
= FALSE
;
442 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
452 Wait for a given event to fire, or for an optional timeout to expire.
454 @param Event The event to wait for
455 @param Timeout An optional timeout value in 100 ns units.
456 @param RefreshInterval Menu refresh interval (in seconds).
458 @retval EFI_SUCCESS Event fired before Timeout expired.
459 @retval EFI_TIME_OUT Timout expired before Event fired.
463 UiWaitForSingleEvent (
465 IN UINT64 Timeout
, OPTIONAL
466 IN UINT8 RefreshInterval OPTIONAL
471 EFI_EVENT TimerEvent
;
472 EFI_EVENT WaitList
[2];
476 // Create a timer event
478 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
479 if (!EFI_ERROR (Status
)) {
481 // Set the timer event
490 // Wait for the original event or the timer
493 WaitList
[1] = TimerEvent
;
494 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
495 gBS
->CloseEvent (TimerEvent
);
498 // If the timer expired, change the return to timed out
500 if (!EFI_ERROR (Status
) && Index
== 1) {
501 Status
= EFI_TIMEOUT
;
506 // Update screen every second
508 if (RefreshInterval
== 0) {
509 Timeout
= ONE_SECOND
;
511 Timeout
= RefreshInterval
* ONE_SECOND
;
515 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
518 // Set the timer event
527 // Wait for the original event or the timer
530 WaitList
[1] = TimerEvent
;
531 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
534 // If the timer expired, update anything that needs a refresh and keep waiting
536 if (!EFI_ERROR (Status
) && Index
== 1) {
537 Status
= EFI_TIMEOUT
;
538 if (RefreshInterval
!= 0) {
539 Status
= RefreshForm ();
543 gBS
->CloseEvent (TimerEvent
);
544 } while (Status
== EFI_TIMEOUT
);
552 Add one menu option by specified description and context.
554 @param String String description for this option.
555 @param Handle Hii handle for the package list.
556 @param Statement Statement of this Menu Option.
557 @param NumberOfLines Display lines for this Menu Option.
558 @param MenuItemCount The index for this Option in the Menu.
560 @retval Pointer Pointer to the added Menu Option.
566 IN EFI_HII_HANDLE Handle
,
567 IN FORM_BROWSER_STATEMENT
*Statement
,
568 IN UINT16 NumberOfLines
,
569 IN UINT16 MenuItemCount
572 UI_MENU_OPTION
*MenuOption
;
579 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
581 // Add three MenuOptions for Date/Time
582 // Data format : [01/02/2004] [11:22:33]
583 // Line number : 0 0 1 0 0 1
588 if (Statement
->Storage
== NULL
) {
590 // For RTC type of date/time, set default refresh interval to be 1 second
592 if (Statement
->RefreshInterval
== 0) {
593 Statement
->RefreshInterval
= 1;
598 for (Index
= 0; Index
< Count
; Index
++) {
599 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
602 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
603 MenuOption
->Description
= String
;
604 MenuOption
->Handle
= Handle
;
605 MenuOption
->ThisTag
= Statement
;
606 MenuOption
->EntryNumber
= MenuItemCount
;
610 // Override LineNumber for the MenuOption in Date/Time sequence
612 MenuOption
->Skip
= 1;
614 MenuOption
->Skip
= NumberOfLines
;
616 MenuOption
->Sequence
= Index
;
618 if (Statement
->GrayOutExpression
!= NULL
) {
619 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
622 switch (Statement
->Operand
) {
623 case EFI_IFR_ORDERED_LIST_OP
:
624 case EFI_IFR_ONE_OF_OP
:
625 case EFI_IFR_NUMERIC_OP
:
626 case EFI_IFR_TIME_OP
:
627 case EFI_IFR_DATE_OP
:
628 case EFI_IFR_CHECKBOX_OP
:
629 case EFI_IFR_PASSWORD_OP
:
630 case EFI_IFR_STRING_OP
:
632 // User could change the value of these items
634 MenuOption
->IsQuestion
= TRUE
;
638 MenuOption
->IsQuestion
= FALSE
;
642 if ((Statement
->ValueExpression
!= NULL
) ||
643 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
644 MenuOption
->ReadOnly
= TRUE
;
647 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
655 Routine used to abstract a generic dialog interface and return the selected key or string
657 @param NumberOfLines The number of lines for the dialog box
658 @param HotKey Defines whether a single character is parsed
659 (TRUE) and returned in KeyValue or a string is
660 returned in StringBuffer. Two special characters
661 are considered when entering a string, a SCAN_ESC
662 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
663 string input and returns
664 @param MaximumStringSize The maximum size in bytes of a typed in string
665 (each character is a CHAR16) and the minimum
666 string returned is two bytes
667 @param StringBuffer The passed in pointer to the buffer which will
668 hold the typed in string if HotKey is FALSE
669 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
670 @param ... A series of (quantity == NumberOfLines) text
671 strings which will be used to construct the dialog
674 @retval EFI_SUCCESS Displayed dialog and received user interaction
675 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
676 (StringBuffer == NULL) && (HotKey == FALSE))
677 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
682 IN UINTN NumberOfLines
,
684 IN UINTN MaximumStringSize
,
685 OUT CHAR16
*StringBuffer
,
686 OUT EFI_INPUT_KEY
*KeyValue
,
695 CHAR16
*BufferedString
;
702 BOOLEAN SelectionComplete
;
704 UINTN CurrentAttribute
;
705 UINTN DimensionsWidth
;
706 UINTN DimensionsHeight
;
708 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
709 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
711 SelectionComplete
= FALSE
;
713 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
714 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
715 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
718 ASSERT (BufferedString
);
720 VA_START (Marker
, KeyValue
);
723 // Zero the outgoing buffer
725 ZeroMem (StringBuffer
, MaximumStringSize
);
728 if (KeyValue
== NULL
) {
729 return EFI_INVALID_PARAMETER
;
732 if (StringBuffer
== NULL
) {
733 return EFI_INVALID_PARAMETER
;
739 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
744 // Determine the largest string in the dialog box
745 // Notice we are starting with 1 since String is the first string
747 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
748 StackString
= VA_ARG (Marker
, CHAR16
*);
750 if (StackString
[0] == L
' ') {
751 InputOffset
= Count
+ 1;
754 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
756 // Size of the string visually and subtract the width by one for the null-terminator
758 LargestString
= (GetStringWidth (StackString
) / 2);
763 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
764 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
771 VA_START (Marker
, KeyValue
);
772 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
776 // Take the first key typed and report it back?
779 Status
= WaitForKeyStroke (&Key
);
780 ASSERT_EFI_ERROR (Status
);
781 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
785 Status
= WaitForKeyStroke (&Key
);
787 switch (Key
.UnicodeChar
) {
789 switch (Key
.ScanCode
) {
791 FreePool (TempString
);
792 FreePool (BufferedString
);
793 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
794 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
795 return EFI_DEVICE_ERROR
;
803 case CHAR_CARRIAGE_RETURN
:
804 SelectionComplete
= TRUE
;
805 FreePool (TempString
);
806 FreePool (BufferedString
);
807 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
808 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
813 if (StringBuffer
[0] != CHAR_NULL
) {
814 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
815 TempString
[Index
] = StringBuffer
[Index
];
818 // Effectively truncate string by 1 character
820 TempString
[Index
- 1] = CHAR_NULL
;
821 StrCpy (StringBuffer
, TempString
);
826 // If it is the beginning of the string, don't worry about checking maximum limits
828 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
829 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
830 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
831 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
832 KeyPad
[0] = Key
.UnicodeChar
;
833 KeyPad
[1] = CHAR_NULL
;
834 StrCat (StringBuffer
, KeyPad
);
835 StrCat (TempString
, KeyPad
);
838 // If the width of the input string is now larger than the screen, we nee to
839 // adjust the index to start printing portions of the string
841 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
843 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
845 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
846 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
851 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
852 BufferedString
[Count
] = StringBuffer
[Index
];
855 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
858 } while (!SelectionComplete
);
861 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
862 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
867 Draw a pop up windows based on the dimension, number of lines and
870 @param RequestedWidth The width of the pop-up.
871 @param NumberOfLines The number of lines.
872 @param Marker The variable argument list for the list of string to be printed.
877 IN UINTN RequestedWidth
,
878 IN UINTN NumberOfLines
,
890 UINTN DimensionsWidth
;
891 UINTN DimensionsHeight
;
893 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
894 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
896 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
898 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
899 RequestedWidth
= DimensionsWidth
- 2;
903 // Subtract the PopUp width from total Columns, allow for one space extra on
904 // each end plus a border.
906 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
907 End
= Start
+ RequestedWidth
+ 1;
909 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
910 Bottom
= Top
+ NumberOfLines
+ 2;
912 Character
= BOXDRAW_DOWN_RIGHT
;
913 PrintCharAt (Start
, Top
, Character
);
914 Character
= BOXDRAW_HORIZONTAL
;
915 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
916 PrintChar (Character
);
919 Character
= BOXDRAW_DOWN_LEFT
;
920 PrintChar (Character
);
921 Character
= BOXDRAW_VERTICAL
;
924 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
925 String
= VA_ARG (Marker
, CHAR16
*);
928 // This will clear the background of the line - we never know who might have been
929 // here before us. This differs from the next clear in that it used the non-reverse
930 // video for normal printing.
932 if (GetStringWidth (String
) / 2 > 1) {
933 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
937 // Passing in a space results in the assumption that this is where typing will occur
939 if (String
[0] == L
' ') {
940 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
944 // Passing in a NULL results in a blank space
946 if (String
[0] == CHAR_NULL
) {
947 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
951 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
955 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
956 PrintCharAt (Start
, Index
+ 1, Character
);
957 PrintCharAt (End
- 1, Index
+ 1, Character
);
960 Character
= BOXDRAW_UP_RIGHT
;
961 PrintCharAt (Start
, Bottom
- 1, Character
);
962 Character
= BOXDRAW_HORIZONTAL
;
963 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
964 PrintChar (Character
);
967 Character
= BOXDRAW_UP_LEFT
;
968 PrintChar (Character
);
972 Draw a pop up windows based on the dimension, number of lines and
975 @param RequestedWidth The width of the pop-up.
976 @param NumberOfLines The number of lines.
977 @param ... A series of text strings that displayed in the pop-up.
981 CreateMultiStringPopUp (
982 IN UINTN RequestedWidth
,
983 IN UINTN NumberOfLines
,
989 VA_START (Marker
, NumberOfLines
);
991 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
998 Update status bar on the bottom of menu.
1000 @param MessageType The type of message to be shown.
1001 @param Flags The flags in Question header.
1002 @param State Set or clear.
1007 IN UINTN MessageType
,
1013 CHAR16
*NvUpdateMessage
;
1014 CHAR16
*InputErrorMessage
;
1016 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1017 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1019 switch (MessageType
) {
1022 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1024 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1025 gScreenDimensions
.BottomRow
- 1,
1030 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1031 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1032 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1035 mInputError
= FALSE
;
1039 case NV_UPDATE_REQUIRED
:
1040 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
1042 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1044 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1045 gScreenDimensions
.BottomRow
- 1,
1048 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1050 gNvUpdateRequired
= TRUE
;
1052 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1053 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1055 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1056 gScreenDimensions
.BottomRow
- 1,
1061 gNvUpdateRequired
= FALSE
;
1066 case REFRESH_STATUS_BAR
:
1068 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
1071 if (gNvUpdateRequired
) {
1072 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1080 FreePool (InputErrorMessage
);
1081 FreePool (NvUpdateMessage
);
1087 Get the supported width for a particular op-code
1089 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1090 @param Handle The handle in the HII database being used
1092 @return Returns the number of CHAR16 characters that is support.
1097 IN FORM_BROWSER_STATEMENT
*Statement
,
1098 IN EFI_HII_HANDLE Handle
1108 // See if the second text parameter is really NULL
1110 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1111 String
= GetToken (Statement
->TextTwo
, Handle
);
1112 Size
= StrLen (String
);
1116 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1117 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1118 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1119 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1120 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1122 // Allow a wide display if text op-code and no secondary text op-code
1124 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1126 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1128 Width
= (UINT16
) gPromptBlockWidth
;
1131 if (Statement
->InSubtitle
) {
1132 Width
-= SUBTITLE_INDENT
;
1139 Will copy LineWidth amount of a string in the OutputString buffer and return the
1140 number of CHAR16 characters that were copied into the OutputString buffer.
1142 @param InputString String description for this option.
1143 @param LineWidth Width of the desired string to extract in CHAR16
1145 @param Index Where in InputString to start the copy process
1146 @param OutputString Buffer to copy the string into
1148 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1153 IN CHAR16
*InputString
,
1154 IN UINT16 LineWidth
,
1155 IN OUT UINTN
*Index
,
1156 OUT CHAR16
**OutputString
1162 if (GetLineByWidthFinished
) {
1163 GetLineByWidthFinished
= FALSE
;
1170 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1173 // Ensure we have got a valid buffer
1175 if (*OutputString
!= NULL
) {
1178 //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.
1179 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1181 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1182 *Index
= *Index
+ 2;
1186 // Fast-forward the string and see if there is a carriage-return in the string
1188 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1192 // Copy the desired LineWidth of data to the output buffer.
1193 // Also make sure that we don't copy more than the string.
1194 // Also make sure that if there are linefeeds, we account for them.
1196 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1197 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1200 // Convert to CHAR16 value and show that we are done with this operation
1202 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1203 if (LineWidth
!= 0) {
1204 GetLineByWidthFinished
= TRUE
;
1207 if (Count2
== LineWidth
) {
1209 // Rewind the string from the maximum size until we see a space to break the line
1211 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1213 if (LineWidth
== 0) {
1221 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1224 // If currently pointing to a space, increment the index to the first non-space character
1227 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1231 *Index
= (UINT16
) (*Index
+ LineWidth
);
1240 Update display lines for a Menu Option.
1242 @param Selection The user's selection.
1243 @param MenuOption The MenuOption to be checked.
1244 @param OptionalString The option string.
1245 @param SkipValue The number of lins to skip.
1249 UpdateOptionSkipLines (
1250 IN UI_MENU_SELECTION
*Selection
,
1251 IN UI_MENU_OPTION
*MenuOption
,
1252 OUT CHAR16
**OptionalString
,
1260 CHAR16
*OutputString
;
1261 CHAR16
*OptionString
;
1264 OptionString
= *OptionalString
;
1265 OutputString
= NULL
;
1267 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1269 if (OptionString
!= NULL
) {
1270 Width
= (UINT16
) gOptionBlockWidth
;
1274 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1276 // If there is more string to process print on the next row and increment the Skip value
1278 if (StrLen (&OptionString
[Index
]) != 0) {
1279 if (SkipValue
== 0) {
1282 // Since the Number of lines for this menu entry may or may not be reflected accurately
1283 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1284 // some testing to ensure we are keeping this in-sync.
1286 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1288 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1294 FreePool (OutputString
);
1295 if (SkipValue
!= 0) {
1303 *OptionalString
= OptionString
;
1308 Check whether this Menu Option could be highlighted.
1310 This is an internal function.
1312 @param MenuOption The MenuOption to be checked.
1314 @retval TRUE This Menu Option is selectable.
1315 @retval FALSE This Menu Option could not be selected.
1320 UI_MENU_OPTION
*MenuOption
1323 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1324 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1333 Determine if the menu is the last menu that can be selected.
1335 This is an internal function.
1337 @param Direction The scroll direction. False is down. True is up.
1338 @param CurrentPos The current focus.
1340 @return FALSE -- the menu isn't the last menu that can be selected.
1341 @return TRUE -- the menu is the last menu that can be selected.
1346 IN BOOLEAN Direction
,
1347 IN LIST_ENTRY
*CurrentPos
1351 UI_MENU_OPTION
*MenuOption
;
1353 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1355 if (Temp
== &gMenuOption
) {
1359 for (; Temp
!= &gMenuOption
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1360 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1361 if (IsSelectable (MenuOption
)) {
1371 Move to next selectable statement.
1373 This is an internal function.
1375 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1376 @param CurrentPosition Current position.
1378 @return The row distance from current MenuOption to next selectable MenuOption.
1382 MoveToNextStatement (
1384 IN OUT LIST_ENTRY
**CurrentPosition
1390 UI_MENU_OPTION
*NextMenuOption
;
1393 Pos
= *CurrentPosition
;
1397 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1398 if (IsSelectable (NextMenuOption
)) {
1401 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1405 Distance
+= NextMenuOption
->Skip
;
1406 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1411 // If we hit end there is still no statement can be focused,
1412 // we go backwards to find the statement can be focused.
1415 Pos
= *CurrentPosition
;
1418 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1419 if (IsSelectable (NextMenuOption
)) {
1422 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1426 Distance
-= NextMenuOption
->Skip
;
1427 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1431 *CurrentPosition
= &NextMenuOption
->Link
;
1437 Adjust Data and Time position accordingly.
1438 Data format : [01/02/2004] [11:22:33]
1439 Line number : 0 0 1 0 0 1
1441 This is an internal function.
1443 @param DirectionUp the up or down direction. False is down. True is
1445 @param CurrentPosition Current position. On return: Point to the last
1446 Option (Year or Second) if up; Point to the first
1447 Option (Month or Hour) if down.
1449 @return Return line number to pad. It is possible that we stand on a zero-advance
1450 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1454 AdjustDateAndTimePosition (
1455 IN BOOLEAN DirectionUp
,
1456 IN OUT LIST_ENTRY
**CurrentPosition
1460 LIST_ENTRY
*NewPosition
;
1461 UI_MENU_OPTION
*MenuOption
;
1462 UINTN PadLineNumber
;
1465 NewPosition
= *CurrentPosition
;
1466 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1468 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1469 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1471 // Calculate the distance from current position to the last Date/Time MenuOption
1474 while (MenuOption
->Skip
== 0) {
1476 NewPosition
= NewPosition
->ForwardLink
;
1477 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1481 NewPosition
= *CurrentPosition
;
1484 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1485 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1486 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1487 // checking can be done.
1489 while (Count
++ < 2) {
1490 NewPosition
= NewPosition
->BackLink
;
1494 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1495 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1496 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1497 // checking can be done.
1499 while (Count
-- > 0) {
1500 NewPosition
= NewPosition
->ForwardLink
;
1504 *CurrentPosition
= NewPosition
;
1507 return PadLineNumber
;
1511 Find HII Handle in the HII database associated with given Device Path.
1513 If DevicePath is NULL, then ASSERT.
1515 @param DevicePath Device Path associated with the HII package list
1518 @retval Handle HII package list Handle associated with the Device
1520 @retval NULL Hii Package list handle is not found.
1525 DevicePathToHiiHandle (
1526 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1530 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1535 EFI_HANDLE DriverHandle
;
1536 EFI_HII_HANDLE
*HiiHandles
;
1537 EFI_HII_HANDLE HiiHandle
;
1539 ASSERT (DevicePath
!= NULL
);
1541 TmpDevicePath
= DevicePath
;
1543 // Locate Device Path Protocol handle buffer
1545 Status
= gBS
->LocateDevicePath (
1546 &gEfiDevicePathProtocolGuid
,
1550 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1555 // Retrieve all HII Handles from HII database
1557 BufferSize
= 0x1000;
1558 HiiHandles
= AllocatePool (BufferSize
);
1559 ASSERT (HiiHandles
!= NULL
);
1560 Status
= mHiiDatabase
->ListPackageLists (
1562 EFI_HII_PACKAGE_TYPE_ALL
,
1567 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1568 FreePool (HiiHandles
);
1569 HiiHandles
= AllocatePool (BufferSize
);
1570 ASSERT (HiiHandles
!= NULL
);
1572 Status
= mHiiDatabase
->ListPackageLists (
1574 EFI_HII_PACKAGE_TYPE_ALL
,
1581 if (EFI_ERROR (Status
)) {
1582 FreePool (HiiHandles
);
1587 // Search Hii Handle by Driver Handle
1590 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1591 for (Index
= 0; Index
< HandleCount
; Index
++) {
1592 Status
= mHiiDatabase
->GetPackageListHandle (
1597 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1598 HiiHandle
= HiiHandles
[Index
];
1603 FreePool (HiiHandles
);
1608 Display menu and wait for user to select one menu option, then return it.
1609 If AutoBoot is enabled, then if user doesn't select any option,
1610 after period of time, it will automatically return the first menu option.
1612 @param Selection Menu selection.
1614 @retval EFI_SUCESSS This function always return successfully for now.
1619 IN OUT UI_MENU_SELECTION
*Selection
1625 UINTN DistanceValue
;
1637 CHAR16
*OptionString
;
1638 CHAR16
*OutputString
;
1639 CHAR16
*FormattedString
;
1651 LIST_ENTRY
*TopOfScreen
;
1652 LIST_ENTRY
*SavedListEntry
;
1653 UI_MENU_OPTION
*MenuOption
;
1654 UI_MENU_OPTION
*NextMenuOption
;
1655 UI_MENU_OPTION
*SavedMenuOption
;
1656 UI_MENU_OPTION
*PreviousMenuOption
;
1657 UI_CONTROL_FLAG ControlFlag
;
1658 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1659 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1660 UI_SCREEN_OPERATION ScreenOperation
;
1661 UINT8 MinRefreshInterval
;
1664 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1665 FORM_BROWSER_STATEMENT
*Statement
;
1667 UINT8
*DevicePathBuffer
;
1669 UI_MENU_LIST
*CurrentMenu
;
1670 UI_MENU_LIST
*MenuList
;
1671 FORM_BROWSER_FORM
*RefForm
;
1673 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1675 Status
= EFI_SUCCESS
;
1676 FormattedString
= NULL
;
1677 OptionString
= NULL
;
1678 ScreenOperation
= UiNoOperation
;
1680 MinRefreshInterval
= 0;
1683 OutputString
= NULL
;
1688 MenuRefreshEntry
= gMenuRefreshHead
;
1690 NextMenuOption
= NULL
;
1691 PreviousMenuOption
= NULL
;
1692 SavedMenuOption
= NULL
;
1695 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1697 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
1698 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1699 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1701 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1702 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1705 Col
= LocalScreen
.LeftColumn
;
1706 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1708 Selection
->TopRow
= TopRow
;
1709 Selection
->BottomRow
= BottomRow
;
1710 Selection
->PromptCol
= Col
;
1711 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1712 Selection
->Statement
= NULL
;
1714 TopOfScreen
= gMenuOption
.ForwardLink
;
1719 // Find current Menu
1721 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
1722 if (CurrentMenu
== NULL
) {
1724 // Current menu not found, add it to the menu tree
1726 CurrentMenu
= UiAddMenuList (NULL
, &Selection
->FormSetGuid
, Selection
->FormId
);
1728 ASSERT (CurrentMenu
!= NULL
);
1730 if (Selection
->QuestionId
== 0) {
1732 // Highlight not specified, fetch it from cached menu
1734 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
1738 // Get user's selection
1740 NewPos
= gMenuOption
.ForwardLink
;
1742 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1743 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1745 ControlFlag
= CfInitialization
;
1746 Selection
->Action
= UI_ACTION_NONE
;
1748 switch (ControlFlag
) {
1749 case CfInitialization
:
1750 if (IsListEmpty (&gMenuOption
)) {
1751 ControlFlag
= CfReadKey
;
1753 ControlFlag
= CfCheckSelection
;
1757 case CfCheckSelection
:
1758 if (Selection
->Action
!= UI_ACTION_NONE
) {
1759 ControlFlag
= CfExit
;
1761 ControlFlag
= CfRepaint
;
1766 ControlFlag
= CfRefreshHighLight
;
1780 LocalScreen
.LeftColumn
,
1781 LocalScreen
.RightColumn
,
1782 TopRow
- SCROLL_ARROW_HEIGHT
,
1783 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1784 FIELD_TEXT
| FIELD_BACKGROUND
1787 UiFreeRefreshList ();
1788 MinRefreshInterval
= 0;
1790 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
1791 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1792 MenuOption
->Row
= Row
;
1793 MenuOption
->Col
= Col
;
1794 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1796 Statement
= MenuOption
->ThisTag
;
1797 if (Statement
->InSubtitle
) {
1798 MenuOption
->Col
+= SUBTITLE_INDENT
;
1801 if (MenuOption
->GrayOut
) {
1802 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1804 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1805 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1809 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1812 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1813 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1814 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1817 // If there is more string to process print on the next row and increment the Skip value
1819 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
1825 FreePool (OutputString
);
1834 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1835 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1836 if (EFI_ERROR (Status
)) {
1838 // Repaint to clear possible error prompt pop-up
1842 ControlFlag
= CfRepaint
;
1846 if (OptionString
!= NULL
) {
1847 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1849 // If leading spaces on OptionString - remove the spaces
1851 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1852 MenuOption
->OptCol
++;
1855 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1856 OptionString
[Count
] = OptionString
[Index
];
1860 OptionString
[Count
] = CHAR_NULL
;
1864 // If Question request refresh, register the op-code
1866 if (Statement
->RefreshInterval
!= 0) {
1868 // Menu will be refreshed at minimal interval of all Questions
1869 // which have refresh request
1871 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1872 MinRefreshInterval
= Statement
->RefreshInterval
;
1875 if (gMenuRefreshHead
== NULL
) {
1876 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1877 ASSERT (MenuRefreshEntry
!= NULL
);
1878 MenuRefreshEntry
->MenuOption
= MenuOption
;
1879 MenuRefreshEntry
->Selection
= Selection
;
1880 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1881 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1882 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1883 gMenuRefreshHead
= MenuRefreshEntry
;
1886 // Advance to the last entry
1888 for (MenuRefreshEntry
= gMenuRefreshHead
;
1889 MenuRefreshEntry
->Next
!= NULL
;
1890 MenuRefreshEntry
= MenuRefreshEntry
->Next
1893 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1894 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1895 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1896 MenuRefreshEntry
->MenuOption
= MenuOption
;
1897 MenuRefreshEntry
->Selection
= Selection
;
1898 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1899 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1900 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1904 Width
= (UINT16
) gOptionBlockWidth
;
1907 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1908 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1909 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1912 // If there is more string to process print on the next row and increment the Skip value
1914 if (StrLen (&OptionString
[Index
]) != 0) {
1918 // Since the Number of lines for this menu entry may or may not be reflected accurately
1919 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1920 // some testing to ensure we are keeping this in-sync.
1922 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1924 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1930 FreePool (OutputString
);
1939 FreePool (OptionString
);
1942 // If this is a text op with secondary text information
1944 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1945 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1947 Width
= (UINT16
) gOptionBlockWidth
;
1950 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1951 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1952 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1955 // If there is more string to process print on the next row and increment the Skip value
1957 if (StrLen (&StringPtr
[Index
]) != 0) {
1961 // Since the Number of lines for this menu entry may or may not be reflected accurately
1962 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1963 // some testing to ensure we are keeping this in-sync.
1965 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1967 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1973 FreePool (OutputString
);
1980 FreePool (StringPtr
);
1984 // Need to handle the bottom of the display
1986 if (MenuOption
->Skip
> 1) {
1987 Row
+= MenuOption
->Skip
- SkipValue
;
1990 Row
+= MenuOption
->Skip
;
1993 if (Row
> BottomRow
) {
1994 if (!ValueIsScroll (FALSE
, Link
)) {
1998 Row
= BottomRow
+ 1;
2003 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2008 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2010 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2011 TopRow
- SCROLL_ARROW_HEIGHT
,
2015 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2019 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2021 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2022 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2026 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2033 case CfRefreshHighLight
:
2035 // MenuOption: Last menu option that need to remove hilight
2036 // MenuOption is set to NULL in Repaint
2037 // NewPos: Current menu option that need to hilight
2039 ControlFlag
= CfUpdateHelpString
;
2042 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2043 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2045 SavedValue
= Repaint
;
2048 if (Selection
->QuestionId
!= 0) {
2049 NewPos
= gMenuOption
.ForwardLink
;
2050 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2052 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &gMenuOption
) {
2053 NewPos
= NewPos
->ForwardLink
;
2054 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2056 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2058 // Target Question found, find its MenuOption
2062 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2063 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2064 Index
+= SavedMenuOption
->Skip
;
2065 Link
= Link
->ForwardLink
;
2068 if (Link
!= NewPos
|| Index
> BottomRow
) {
2070 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2073 for (Index
= TopRow
; Index
<= BottomRow
; ) {
2074 Link
= Link
->BackLink
;
2075 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2076 Index
+= SavedMenuOption
->Skip
;
2078 TopOfScreen
= Link
->ForwardLink
;
2082 ControlFlag
= CfRepaint
;
2087 // Target Question not found, highlight the default menu option
2089 NewPos
= TopOfScreen
;
2092 Selection
->QuestionId
= 0;
2095 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2096 if (MenuOption
!= NULL
) {
2098 // Remove highlight on last Menu Option
2100 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2101 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2102 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2103 if (OptionString
!= NULL
) {
2104 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2105 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2108 // If leading spaces on OptionString - remove the spaces
2110 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2113 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2114 OptionString
[Count
] = OptionString
[Index
];
2118 OptionString
[Count
] = CHAR_NULL
;
2121 Width
= (UINT16
) gOptionBlockWidth
;
2122 OriginalRow
= MenuOption
->Row
;
2124 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2125 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2126 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2129 // If there is more string to process print on the next row and increment the Skip value
2131 if (StrLen (&OptionString
[Index
]) != 0) {
2135 FreePool (OutputString
);
2138 MenuOption
->Row
= OriginalRow
;
2140 FreePool (OptionString
);
2143 if (MenuOption
->GrayOut
) {
2144 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2145 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2146 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
2149 OriginalRow
= MenuOption
->Row
;
2150 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2152 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2153 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2154 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2157 // If there is more string to process print on the next row and increment the Skip value
2159 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2163 FreePool (OutputString
);
2166 MenuOption
->Row
= OriginalRow
;
2167 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2173 // This is only possible if we entered this page and the first menu option is
2174 // a "non-menu" item. In that case, force it UiDown
2176 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2177 if (!IsSelectable (MenuOption
)) {
2178 ASSERT (ScreenOperation
== UiNoOperation
);
2179 ScreenOperation
= UiDown
;
2180 ControlFlag
= CfScreenOperation
;
2185 // This is the current selected statement
2187 Statement
= MenuOption
->ThisTag
;
2188 Selection
->Statement
= Statement
;
2190 // Record highlight for current menu
2192 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2195 // Set reverse attribute
2197 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
2198 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2201 // Assuming that we have a refresh linked-list created, lets annotate the
2202 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2203 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2205 if (gMenuRefreshHead
!= NULL
) {
2206 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2207 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
2208 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2209 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
2214 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2215 if (OptionString
!= NULL
) {
2216 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2218 // If leading spaces on OptionString - remove the spaces
2220 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2223 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2224 OptionString
[Count
] = OptionString
[Index
];
2228 OptionString
[Count
] = CHAR_NULL
;
2230 Width
= (UINT16
) gOptionBlockWidth
;
2232 OriginalRow
= MenuOption
->Row
;
2234 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2235 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2236 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2239 // If there is more string to process print on the next row and increment the Skip value
2241 if (StrLen (&OptionString
[Index
]) != 0) {
2245 FreePool (OutputString
);
2248 MenuOption
->Row
= OriginalRow
;
2250 FreePool (OptionString
);
2253 OriginalRow
= MenuOption
->Row
;
2255 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2257 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2258 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2259 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2262 // If there is more string to process print on the next row and increment the Skip value
2264 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2268 FreePool (OutputString
);
2271 MenuOption
->Row
= OriginalRow
;
2276 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2279 // Clear reverse attribute
2281 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2284 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2285 // if we didn't break halfway when process CfRefreshHighLight.
2287 Repaint
= SavedValue
;
2290 case CfUpdateHelpString
:
2291 ControlFlag
= CfPrepareToReadKey
;
2293 if (Repaint
|| NewLine
) {
2295 // Don't print anything if it is a NULL help token
2297 if (MenuOption
->ThisTag
->Help
== 0) {
2300 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2303 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2305 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2307 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2309 // Pad String with spaces to simulate a clearing of the previous line
2311 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2312 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2316 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2318 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2323 // Reset this flag every time we finish using it.
2329 case CfPrepareToReadKey
:
2330 ControlFlag
= CfReadKey
;
2331 ScreenOperation
= UiNoOperation
;
2335 ControlFlag
= CfScreenOperation
;
2338 // Wait for user's selection
2341 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2342 } while (Status
== EFI_TIMEOUT
);
2344 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2346 // IFR is updated in Callback of refresh opcode, re-parse it
2348 Selection
->Statement
= NULL
;
2352 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2354 // If we encounter error, continue to read another key in.
2356 if (EFI_ERROR (Status
)) {
2357 ControlFlag
= CfReadKey
;
2361 switch (Key
.UnicodeChar
) {
2362 case CHAR_CARRIAGE_RETURN
:
2363 ScreenOperation
= UiSelect
;
2368 // We will push the adjustment of these numeric values directly to the input handler
2369 // NOTE: we won't handle manual input numeric
2373 Statement
= MenuOption
->ThisTag
;
2374 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2375 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2376 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2378 if (Key
.UnicodeChar
== '+') {
2379 gDirection
= SCAN_RIGHT
;
2381 gDirection
= SCAN_LEFT
;
2383 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2384 if (EFI_ERROR (Status
)) {
2386 // Repaint to clear possible error prompt pop-up
2391 if (OptionString
!= NULL
) {
2392 FreePool (OptionString
);
2398 ScreenOperation
= UiUp
;
2403 ScreenOperation
= UiDown
;
2407 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2408 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2409 ScreenOperation
= UiSelect
;
2415 if (((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2416 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2419 // If the function key has been disabled, just ignore the key.
2422 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2423 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2424 if (Key
.ScanCode
== SCAN_F9
) {
2426 // Reset to standard default
2428 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2430 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2439 case CfScreenOperation
:
2440 if (ScreenOperation
!= UiReset
) {
2442 // If the screen has no menu items, and the user didn't select UiReset
2443 // ignore the selection and go back to reading keys.
2445 if (IsListEmpty (&gMenuOption
)) {
2446 ControlFlag
= CfReadKey
;
2450 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2452 for (Link
= gMenuOption
.ForwardLink
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2453 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2454 if (IsSelectable (NextMenuOption
)) {
2459 if (Link
== &gMenuOption
) {
2460 ControlFlag
= CfPrepareToReadKey
;
2466 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2469 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2470 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2477 ControlFlag
= CfCheckSelection
;
2479 Statement
= MenuOption
->ThisTag
;
2480 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2481 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2482 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2483 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2488 // Keep highlight on current MenuOption
2490 Selection
->QuestionId
= Statement
->QuestionId
;
2492 switch (Statement
->Operand
) {
2493 case EFI_IFR_REF_OP
:
2494 if (Statement
->RefDevicePath
!= 0) {
2496 // Goto another Hii Package list
2498 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2500 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2501 if (StringPtr
== NULL
) {
2503 // No device path string not found, exit
2505 Selection
->Action
= UI_ACTION_EXIT
;
2506 Selection
->Statement
= NULL
;
2509 BufferSize
= StrLen (StringPtr
) / 2;
2510 DevicePath
= AllocatePool (BufferSize
);
2513 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2515 DevicePathBuffer
= (UINT8
*) DevicePath
;
2516 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
2517 TemStr
[0] = StringPtr
[Index
];
2518 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
2519 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
2521 // Invalid Hex Char as the tail.
2525 if ((Index
& 1) == 0) {
2526 DevicePathBuffer
[Index
/2] = DigitUint8
;
2528 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2532 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2533 if (Selection
->Handle
== NULL
) {
2535 // If target Hii Handle not found, exit
2537 Selection
->Action
= UI_ACTION_EXIT
;
2538 Selection
->Statement
= NULL
;
2542 FreePool (StringPtr
);
2543 FreePool (DevicePath
);
2545 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2546 Selection
->FormId
= Statement
->RefFormId
;
2547 Selection
->QuestionId
= Statement
->RefQuestionId
;
2548 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2550 // Goto another Formset, check for uncommitted data
2552 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2554 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2555 Selection
->FormId
= Statement
->RefFormId
;
2556 Selection
->QuestionId
= Statement
->RefQuestionId
;
2557 } else if (Statement
->RefFormId
!= 0) {
2559 // Check whether target From is suppressed.
2561 RefForm
= IdToForm (Selection
->FormSet
, Statement
->RefFormId
);
2563 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2564 Status
= EvaluateExpression (Selection
->FormSet
, RefForm
, RefForm
->SuppressExpression
);
2565 if (EFI_ERROR (Status
)) {
2569 if (RefForm
->SuppressExpression
->Result
.Value
.b
) {
2571 // Form is suppressed.
2574 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2575 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2583 // Goto another form inside this formset,
2585 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2588 // Link current form so that we can always go back when someone hits the ESC
2590 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Statement
->RefFormId
);
2591 if (MenuList
== NULL
) {
2592 MenuList
= UiAddMenuList (CurrentMenu
, &Selection
->FormSetGuid
, Statement
->RefFormId
);
2595 Selection
->FormId
= Statement
->RefFormId
;
2596 Selection
->QuestionId
= Statement
->RefQuestionId
;
2597 } else if (Statement
->RefQuestionId
!= 0) {
2599 // Goto another Question
2601 Selection
->QuestionId
= Statement
->RefQuestionId
;
2603 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2604 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2613 case EFI_IFR_ACTION_OP
:
2615 // Process the Config string <ConfigResp>
2617 Status
= ProcessQuestionConfig (Selection
, Statement
);
2619 if (EFI_ERROR (Status
)) {
2624 // The action button may change some Question value, so refresh the form
2626 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2629 case EFI_IFR_RESET_BUTTON_OP
:
2631 // Reset Question to default value specified by DefaultId
2633 ControlFlag
= CfUiDefault
;
2634 DefaultId
= Statement
->DefaultId
;
2639 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2641 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
2642 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2644 if (EFI_ERROR (Status
)) {
2647 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2649 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2652 if (OptionString
!= NULL
) {
2653 FreePool (OptionString
);
2661 // We come here when someone press ESC
2663 ControlFlag
= CfCheckSelection
;
2665 if (CurrentMenu
->Parent
!= NULL
) {
2667 // we have a parent, so go to the parent menu
2669 if (CompareGuid (&CurrentMenu
->FormSetGuid
, &CurrentMenu
->Parent
->FormSetGuid
)) {
2671 // The parent menu and current menu are in the same formset
2673 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2675 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2677 Selection
->Statement
= NULL
;
2679 Selection
->FormId
= CurrentMenu
->Parent
->FormId
;
2680 Selection
->QuestionId
= CurrentMenu
->Parent
->QuestionId
;
2683 // Clear highlight record for this menu
2685 CurrentMenu
->QuestionId
= 0;
2689 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
) {
2691 // We never exit FrontPage, so skip the ESC
2693 Selection
->Action
= UI_ACTION_NONE
;
2698 // We are going to leave current FormSet, so check uncommited data in this FormSet
2700 if (gNvUpdateRequired
) {
2701 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2703 YesResponse
= gYesResponse
[0];
2704 NoResponse
= gNoResponse
[0];
2707 // If NV flag is up, prompt user
2710 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveChanges
, gAreYouSure
, gEmptyString
);
2713 (Key
.ScanCode
!= SCAN_ESC
) &&
2714 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2715 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2718 if (Key
.ScanCode
== SCAN_ESC
) {
2720 // User hits the ESC key
2725 Selection
->Action
= UI_ACTION_NONE
;
2730 // If the user hits the YesResponse key
2732 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2733 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2737 Selection
->Action
= UI_ACTION_EXIT
;
2738 Selection
->Statement
= NULL
;
2739 CurrentMenu
->QuestionId
= 0;
2744 ControlFlag
= CfCheckSelection
;
2745 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2746 if (MenuOption
->Sequence
!= 0) {
2748 // In the middle or tail of the Date/Time op-code set, go left.
2750 NewPos
= NewPos
->BackLink
;
2756 ControlFlag
= CfCheckSelection
;
2757 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2758 if (MenuOption
->Sequence
!= 2) {
2760 // In the middle or tail of the Date/Time op-code set, go left.
2762 NewPos
= NewPos
->ForwardLink
;
2768 ControlFlag
= CfCheckSelection
;
2770 SavedListEntry
= TopOfScreen
;
2772 if (NewPos
->BackLink
!= &gMenuOption
) {
2775 // Adjust Date/Time position before we advance forward.
2777 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2780 // Caution that we have already rewind to the top, don't go backward in this situation.
2782 if (NewPos
->BackLink
!= &gMenuOption
) {
2783 NewPos
= NewPos
->BackLink
;
2786 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2787 DistanceValue
= PreviousMenuOption
->Skip
;
2790 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2791 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2792 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2793 // checking can be done.
2795 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2798 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2799 // don't worry about a redraw.
2801 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2803 TopOfScreen
= NewPos
;
2806 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2807 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2808 DistanceValue
+= PreviousMenuOption
->Skip
;
2810 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2811 if (Difference
> 0) {
2813 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2815 TopOfScreen
= NewPos
;
2821 if (Difference
< 0) {
2823 // We want to goto previous MenuOption, but finally we go down.
2824 // it means that we hit the begining MenuOption that can be focused
2825 // so we simply scroll to the top
2827 if (SavedListEntry
!= gMenuOption
.ForwardLink
) {
2828 TopOfScreen
= gMenuOption
.ForwardLink
;
2834 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2836 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2838 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2840 SavedMenuOption
= MenuOption
;
2841 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2842 if (!IsSelectable (MenuOption
)) {
2844 // If we are at the end of the list and sitting on a text op, we need to more forward
2846 ScreenOperation
= UiDown
;
2847 ControlFlag
= CfScreenOperation
;
2851 MenuOption
= SavedMenuOption
;
2856 ControlFlag
= CfCheckSelection
;
2858 if (NewPos
->BackLink
== &gMenuOption
) {
2867 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2869 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
2870 Index
= Index
- PreviousMenuOption
->Skip
;
2871 Link
= Link
->BackLink
;
2872 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2876 Difference
= MoveToNextStatement (TRUE
, &Link
);
2877 if (Difference
> 0) {
2879 // The focus MenuOption is above the TopOfScreen
2882 } else if (Difference
< 0) {
2884 // This happens when there is no MenuOption can be focused from
2885 // Current MenuOption to the first MenuOption
2887 TopOfScreen
= gMenuOption
.ForwardLink
;
2889 Index
+= Difference
;
2890 if (Index
< TopRow
) {
2894 if (NewPos
== Link
) {
2902 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2903 // Don't do this when we are already in the first page.
2905 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2906 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2910 ControlFlag
= CfCheckSelection
;
2912 if (NewPos
->ForwardLink
== &gMenuOption
) {
2921 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2923 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
2924 Index
= Index
+ NextMenuOption
->Skip
;
2925 Link
= Link
->ForwardLink
;
2926 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2929 Index
+= MoveToNextStatement (FALSE
, &Link
);
2930 if (Index
> BottomRow
) {
2932 // There are more MenuOption needing scrolling
2937 if (NewPos
== Link
&& Index
<= BottomRow
) {
2939 // Finally we know that NewPos is the last MenuOption can be focused.
2948 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2949 // Don't do this when we are already in the last page.
2951 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2952 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2956 ControlFlag
= CfCheckSelection
;
2958 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2959 // to be one that progresses to the next set of op-codes, we need to advance to the last
2960 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2961 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2962 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2963 // the Date/Time op-code.
2965 SavedListEntry
= NewPos
;
2966 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2968 if (NewPos
->ForwardLink
!= &gMenuOption
) {
2969 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2971 NewPos
= NewPos
->ForwardLink
;
2972 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2974 DistanceValue
+= NextMenuOption
->Skip
;
2975 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2977 // An option might be multi-line, so we need to reflect that data in the overall skip value
2979 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2981 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2982 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2983 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2984 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2990 // If we are going to scroll, update TopOfScreen
2992 if (Temp
> BottomRow
) {
2995 // Is the current top of screen a zero-advance op-code?
2996 // If so, keep moving forward till we hit a >0 advance op-code
2998 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3001 // If bottom op-code is more than one line or top op-code is more than one line
3003 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3005 // Is the bottom op-code greater than or equal in size to the top op-code?
3007 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3009 // Skip the top op-code
3011 TopOfScreen
= TopOfScreen
->ForwardLink
;
3012 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3014 OldSkipValue
= Difference
;
3016 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3019 // If we have a remainder, skip that many more op-codes until we drain the remainder
3022 Difference
>= (INTN
) SavedMenuOption
->Skip
;
3023 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
3026 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3028 TopOfScreen
= TopOfScreen
->ForwardLink
;
3029 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3030 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
3031 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
3034 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
3035 TopOfScreen
= TopOfScreen
->ForwardLink
;
3036 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3037 Difference
= SavedMenuOption
->Skip
- Difference
;
3043 // Since we will act on this op-code in the next routine, and increment the
3044 // SkipValue, set the skips to one less than what is required.
3046 SkipValue
= Difference
- 1;
3050 // Since we will act on this op-code in the next routine, and increment the
3051 // SkipValue, set the skips to one less than what is required.
3053 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3056 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3057 TopOfScreen
= TopOfScreen
->ForwardLink
;
3060 SkipValue
= OldSkipValue
;
3064 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3065 // Let's set a skip flag to smoothly scroll the top of the screen.
3067 if (SavedMenuOption
->Skip
> 1) {
3068 if (SavedMenuOption
== NextMenuOption
) {
3075 TopOfScreen
= TopOfScreen
->ForwardLink
;
3077 } while (SavedMenuOption
->Skip
== 0);
3080 OldSkipValue
= SkipValue
;
3083 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3085 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3088 SavedMenuOption
= MenuOption
;
3089 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3090 if (!IsSelectable (MenuOption
)) {
3092 // If we are at the end of the list and sitting on a text op, we need to more forward
3094 ScreenOperation
= UiUp
;
3095 ControlFlag
= CfScreenOperation
;
3099 MenuOption
= SavedMenuOption
;
3101 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3103 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3108 ControlFlag
= CfCheckSelection
;
3113 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
3115 if (!EFI_ERROR (Status
)) {
3116 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3117 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3120 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3121 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3129 ControlFlag
= CfCheckSelection
;
3130 if (!Selection
->FormEditable
) {
3132 // This Form is not editable, ignore the F9 (reset to default)
3137 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
3139 if (!EFI_ERROR (Status
)) {
3140 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3141 Selection
->Statement
= NULL
;
3144 // Show NV update flag on status bar
3146 gNvUpdateRequired
= TRUE
;
3150 case CfUiNoOperation
:
3151 ControlFlag
= CfCheckSelection
;
3155 UiFreeRefreshList ();
3157 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3158 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3159 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3160 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");