2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2010, 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
;
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
683 IN UINTN NumberOfLines
,
685 IN UINTN MaximumStringSize
,
686 OUT CHAR16
*StringBuffer
,
687 OUT EFI_INPUT_KEY
*KeyValue
,
696 CHAR16
*BufferedString
;
703 BOOLEAN SelectionComplete
;
705 UINTN CurrentAttribute
;
706 UINTN DimensionsWidth
;
707 UINTN DimensionsHeight
;
709 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
710 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
712 SelectionComplete
= FALSE
;
714 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
715 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
716 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
719 ASSERT (BufferedString
);
721 VA_START (Marker
, KeyValue
);
724 // Zero the outgoing buffer
726 ZeroMem (StringBuffer
, MaximumStringSize
);
729 if (KeyValue
== NULL
) {
730 return EFI_INVALID_PARAMETER
;
733 if (StringBuffer
== NULL
) {
734 return EFI_INVALID_PARAMETER
;
740 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
745 // Determine the largest string in the dialog box
746 // Notice we are starting with 1 since String is the first string
748 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
749 StackString
= VA_ARG (Marker
, CHAR16
*);
751 if (StackString
[0] == L
' ') {
752 InputOffset
= Count
+ 1;
755 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
757 // Size of the string visually and subtract the width by one for the null-terminator
759 LargestString
= (GetStringWidth (StackString
) / 2);
764 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
765 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
772 VA_START (Marker
, KeyValue
);
773 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
777 // Take the first key typed and report it back?
780 Status
= WaitForKeyStroke (&Key
);
781 ASSERT_EFI_ERROR (Status
);
782 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
786 Status
= WaitForKeyStroke (&Key
);
788 switch (Key
.UnicodeChar
) {
790 switch (Key
.ScanCode
) {
792 FreePool (TempString
);
793 FreePool (BufferedString
);
794 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
795 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
796 return EFI_DEVICE_ERROR
;
804 case CHAR_CARRIAGE_RETURN
:
805 SelectionComplete
= TRUE
;
806 FreePool (TempString
);
807 FreePool (BufferedString
);
808 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
809 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
814 if (StringBuffer
[0] != CHAR_NULL
) {
815 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
816 TempString
[Index
] = StringBuffer
[Index
];
819 // Effectively truncate string by 1 character
821 TempString
[Index
- 1] = CHAR_NULL
;
822 StrCpy (StringBuffer
, TempString
);
827 // If it is the beginning of the string, don't worry about checking maximum limits
829 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
830 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
831 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
832 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
833 KeyPad
[0] = Key
.UnicodeChar
;
834 KeyPad
[1] = CHAR_NULL
;
835 StrCat (StringBuffer
, KeyPad
);
836 StrCat (TempString
, KeyPad
);
839 // If the width of the input string is now larger than the screen, we nee to
840 // adjust the index to start printing portions of the string
842 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
844 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
846 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
847 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
852 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
853 BufferedString
[Count
] = StringBuffer
[Index
];
856 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
859 } while (!SelectionComplete
);
862 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
863 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
868 Draw a pop up windows based on the dimension, number of lines and
871 @param RequestedWidth The width of the pop-up.
872 @param NumberOfLines The number of lines.
873 @param Marker The variable argument list for the list of string to be printed.
878 IN UINTN RequestedWidth
,
879 IN UINTN NumberOfLines
,
891 UINTN DimensionsWidth
;
892 UINTN DimensionsHeight
;
894 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
895 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
897 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
899 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
900 RequestedWidth
= DimensionsWidth
- 2;
904 // Subtract the PopUp width from total Columns, allow for one space extra on
905 // each end plus a border.
907 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
908 End
= Start
+ RequestedWidth
+ 1;
910 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
911 Bottom
= Top
+ NumberOfLines
+ 2;
913 Character
= BOXDRAW_DOWN_RIGHT
;
914 PrintCharAt (Start
, Top
, Character
);
915 Character
= BOXDRAW_HORIZONTAL
;
916 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
917 PrintChar (Character
);
920 Character
= BOXDRAW_DOWN_LEFT
;
921 PrintChar (Character
);
922 Character
= BOXDRAW_VERTICAL
;
925 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
926 String
= VA_ARG (Marker
, CHAR16
*);
929 // This will clear the background of the line - we never know who might have been
930 // here before us. This differs from the next clear in that it used the non-reverse
931 // video for normal printing.
933 if (GetStringWidth (String
) / 2 > 1) {
934 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
938 // Passing in a space results in the assumption that this is where typing will occur
940 if (String
[0] == L
' ') {
941 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
945 // Passing in a NULL results in a blank space
947 if (String
[0] == CHAR_NULL
) {
948 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
952 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
956 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
957 PrintCharAt (Start
, Index
+ 1, Character
);
958 PrintCharAt (End
- 1, Index
+ 1, Character
);
961 Character
= BOXDRAW_UP_RIGHT
;
962 PrintCharAt (Start
, Bottom
- 1, Character
);
963 Character
= BOXDRAW_HORIZONTAL
;
964 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
965 PrintChar (Character
);
968 Character
= BOXDRAW_UP_LEFT
;
969 PrintChar (Character
);
973 Draw a pop up windows based on the dimension, number of lines and
976 @param RequestedWidth The width of the pop-up.
977 @param NumberOfLines The number of lines.
978 @param ... A series of text strings that displayed in the pop-up.
983 CreateMultiStringPopUp (
984 IN UINTN RequestedWidth
,
985 IN UINTN NumberOfLines
,
991 VA_START (Marker
, NumberOfLines
);
993 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1000 Update status bar on the bottom of menu.
1002 @param MessageType The type of message to be shown.
1003 @param Flags The flags in Question header.
1004 @param State Set or clear.
1009 IN UINTN MessageType
,
1015 CHAR16
*NvUpdateMessage
;
1016 CHAR16
*InputErrorMessage
;
1018 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1019 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1021 switch (MessageType
) {
1024 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1026 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1027 gScreenDimensions
.BottomRow
- 1,
1032 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1033 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1034 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1037 mInputError
= FALSE
;
1041 case NV_UPDATE_REQUIRED
:
1042 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
1044 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1046 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1047 gScreenDimensions
.BottomRow
- 1,
1050 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1052 gNvUpdateRequired
= TRUE
;
1054 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1055 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1057 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1058 gScreenDimensions
.BottomRow
- 1,
1063 gNvUpdateRequired
= FALSE
;
1068 case REFRESH_STATUS_BAR
:
1070 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
1073 if (gNvUpdateRequired
) {
1074 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1082 FreePool (InputErrorMessage
);
1083 FreePool (NvUpdateMessage
);
1089 Get the supported width for a particular op-code
1091 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1092 @param Handle The handle in the HII database being used
1094 @return Returns the number of CHAR16 characters that is support.
1099 IN FORM_BROWSER_STATEMENT
*Statement
,
1100 IN EFI_HII_HANDLE Handle
1110 // See if the second text parameter is really NULL
1112 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1113 String
= GetToken (Statement
->TextTwo
, Handle
);
1114 Size
= StrLen (String
);
1118 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1119 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1120 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1121 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1122 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1124 // Allow a wide display if text op-code and no secondary text op-code
1126 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1128 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1130 Width
= (UINT16
) gPromptBlockWidth
;
1133 if (Statement
->InSubtitle
) {
1134 Width
-= SUBTITLE_INDENT
;
1141 Will copy LineWidth amount of a string in the OutputString buffer and return the
1142 number of CHAR16 characters that were copied into the OutputString buffer.
1144 @param InputString String description for this option.
1145 @param LineWidth Width of the desired string to extract in CHAR16
1147 @param Index Where in InputString to start the copy process
1148 @param OutputString Buffer to copy the string into
1150 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1155 IN CHAR16
*InputString
,
1156 IN UINT16 LineWidth
,
1157 IN OUT UINTN
*Index
,
1158 OUT CHAR16
**OutputString
1164 if (GetLineByWidthFinished
) {
1165 GetLineByWidthFinished
= FALSE
;
1172 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1175 // Ensure we have got a valid buffer
1177 if (*OutputString
!= NULL
) {
1180 //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.
1181 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1183 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1184 *Index
= *Index
+ 2;
1188 // Fast-forward the string and see if there is a carriage-return in the string
1190 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1194 // Copy the desired LineWidth of data to the output buffer.
1195 // Also make sure that we don't copy more than the string.
1196 // Also make sure that if there are linefeeds, we account for them.
1198 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1199 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1202 // Convert to CHAR16 value and show that we are done with this operation
1204 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1205 if (LineWidth
!= 0) {
1206 GetLineByWidthFinished
= TRUE
;
1209 if (Count2
== LineWidth
) {
1211 // Rewind the string from the maximum size until we see a space to break the line
1213 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1215 if (LineWidth
== 0) {
1223 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1226 // If currently pointing to a space, increment the index to the first non-space character
1229 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1233 *Index
= (UINT16
) (*Index
+ LineWidth
);
1242 Update display lines for a Menu Option.
1244 @param Selection The user's selection.
1245 @param MenuOption The MenuOption to be checked.
1246 @param OptionalString The option string.
1247 @param SkipValue The number of lins to skip.
1251 UpdateOptionSkipLines (
1252 IN UI_MENU_SELECTION
*Selection
,
1253 IN UI_MENU_OPTION
*MenuOption
,
1254 OUT CHAR16
**OptionalString
,
1262 CHAR16
*OutputString
;
1263 CHAR16
*OptionString
;
1266 OptionString
= *OptionalString
;
1267 OutputString
= NULL
;
1269 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1271 if (OptionString
!= NULL
) {
1272 Width
= (UINT16
) gOptionBlockWidth
;
1276 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1278 // If there is more string to process print on the next row and increment the Skip value
1280 if (StrLen (&OptionString
[Index
]) != 0) {
1281 if (SkipValue
== 0) {
1284 // Since the Number of lines for this menu entry may or may not be reflected accurately
1285 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1286 // some testing to ensure we are keeping this in-sync.
1288 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1290 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1296 FreePool (OutputString
);
1297 if (SkipValue
!= 0) {
1305 *OptionalString
= OptionString
;
1310 Check whether this Menu Option could be highlighted.
1312 This is an internal function.
1314 @param MenuOption The MenuOption to be checked.
1316 @retval TRUE This Menu Option is selectable.
1317 @retval FALSE This Menu Option could not be selected.
1322 UI_MENU_OPTION
*MenuOption
1325 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1326 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1335 Determine if the menu is the last menu that can be selected.
1337 This is an internal function.
1339 @param Direction The scroll direction. False is down. True is up.
1340 @param CurrentPos The current focus.
1342 @return FALSE -- the menu isn't the last menu that can be selected.
1343 @return TRUE -- the menu is the last menu that can be selected.
1348 IN BOOLEAN Direction
,
1349 IN LIST_ENTRY
*CurrentPos
1353 UI_MENU_OPTION
*MenuOption
;
1355 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1357 if (Temp
== &gMenuOption
) {
1361 for (; Temp
!= &gMenuOption
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1362 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1363 if (IsSelectable (MenuOption
)) {
1373 Move to next selectable statement.
1375 This is an internal function.
1377 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1378 @param CurrentPosition Current position.
1380 @return The row distance from current MenuOption to next selectable MenuOption.
1384 MoveToNextStatement (
1386 IN OUT LIST_ENTRY
**CurrentPosition
1392 UI_MENU_OPTION
*NextMenuOption
;
1395 Pos
= *CurrentPosition
;
1399 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1400 if (IsSelectable (NextMenuOption
)) {
1403 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1407 Distance
+= NextMenuOption
->Skip
;
1408 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1413 // If we hit end there is still no statement can be focused,
1414 // we go backwards to find the statement can be focused.
1417 Pos
= *CurrentPosition
;
1420 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1421 if (IsSelectable (NextMenuOption
)) {
1424 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1428 Distance
-= NextMenuOption
->Skip
;
1429 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1433 *CurrentPosition
= &NextMenuOption
->Link
;
1439 Adjust Data and Time position accordingly.
1440 Data format : [01/02/2004] [11:22:33]
1441 Line number : 0 0 1 0 0 1
1443 This is an internal function.
1445 @param DirectionUp the up or down direction. False is down. True is
1447 @param CurrentPosition Current position. On return: Point to the last
1448 Option (Year or Second) if up; Point to the first
1449 Option (Month or Hour) if down.
1451 @return Return line number to pad. It is possible that we stand on a zero-advance
1452 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1456 AdjustDateAndTimePosition (
1457 IN BOOLEAN DirectionUp
,
1458 IN OUT LIST_ENTRY
**CurrentPosition
1462 LIST_ENTRY
*NewPosition
;
1463 UI_MENU_OPTION
*MenuOption
;
1464 UINTN PadLineNumber
;
1467 NewPosition
= *CurrentPosition
;
1468 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1470 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1471 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1473 // Calculate the distance from current position to the last Date/Time MenuOption
1476 while (MenuOption
->Skip
== 0) {
1478 NewPosition
= NewPosition
->ForwardLink
;
1479 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1483 NewPosition
= *CurrentPosition
;
1486 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1487 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1488 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1489 // checking can be done.
1491 while (Count
++ < 2) {
1492 NewPosition
= NewPosition
->BackLink
;
1496 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1497 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1498 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1499 // checking can be done.
1501 while (Count
-- > 0) {
1502 NewPosition
= NewPosition
->ForwardLink
;
1506 *CurrentPosition
= NewPosition
;
1509 return PadLineNumber
;
1513 Find HII Handle in the HII database associated with given Device Path.
1515 If DevicePath is NULL, then ASSERT.
1517 @param DevicePath Device Path associated with the HII package list
1520 @retval Handle HII package list Handle associated with the Device
1522 @retval NULL Hii Package list handle is not found.
1527 DevicePathToHiiHandle (
1528 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1532 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1537 EFI_HANDLE DriverHandle
;
1538 EFI_HII_HANDLE
*HiiHandles
;
1539 EFI_HII_HANDLE HiiHandle
;
1541 ASSERT (DevicePath
!= NULL
);
1543 TmpDevicePath
= DevicePath
;
1545 // Locate Device Path Protocol handle buffer
1547 Status
= gBS
->LocateDevicePath (
1548 &gEfiDevicePathProtocolGuid
,
1552 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1557 // Retrieve all HII Handles from HII database
1559 BufferSize
= 0x1000;
1560 HiiHandles
= AllocatePool (BufferSize
);
1561 ASSERT (HiiHandles
!= NULL
);
1562 Status
= mHiiDatabase
->ListPackageLists (
1564 EFI_HII_PACKAGE_TYPE_ALL
,
1569 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1570 FreePool (HiiHandles
);
1571 HiiHandles
= AllocatePool (BufferSize
);
1572 ASSERT (HiiHandles
!= NULL
);
1574 Status
= mHiiDatabase
->ListPackageLists (
1576 EFI_HII_PACKAGE_TYPE_ALL
,
1583 if (EFI_ERROR (Status
)) {
1584 FreePool (HiiHandles
);
1589 // Search Hii Handle by Driver Handle
1592 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1593 for (Index
= 0; Index
< HandleCount
; Index
++) {
1594 Status
= mHiiDatabase
->GetPackageListHandle (
1599 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1600 HiiHandle
= HiiHandles
[Index
];
1605 FreePool (HiiHandles
);
1610 Display menu and wait for user to select one menu option, then return it.
1611 If AutoBoot is enabled, then if user doesn't select any option,
1612 after period of time, it will automatically return the first menu option.
1614 @param Selection Menu selection.
1616 @retval EFI_SUCESSS This function always return successfully for now.
1621 IN OUT UI_MENU_SELECTION
*Selection
1627 UINTN DistanceValue
;
1639 CHAR16
*OptionString
;
1640 CHAR16
*OutputString
;
1641 CHAR16
*FormattedString
;
1653 LIST_ENTRY
*TopOfScreen
;
1654 LIST_ENTRY
*SavedListEntry
;
1655 UI_MENU_OPTION
*MenuOption
;
1656 UI_MENU_OPTION
*NextMenuOption
;
1657 UI_MENU_OPTION
*SavedMenuOption
;
1658 UI_MENU_OPTION
*PreviousMenuOption
;
1659 UI_CONTROL_FLAG ControlFlag
;
1660 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1661 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1662 UI_SCREEN_OPERATION ScreenOperation
;
1663 UINT8 MinRefreshInterval
;
1666 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1667 FORM_BROWSER_STATEMENT
*Statement
;
1669 UINT8
*DevicePathBuffer
;
1671 UI_MENU_LIST
*CurrentMenu
;
1672 UI_MENU_LIST
*MenuList
;
1673 FORM_BROWSER_FORM
*RefForm
;
1675 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1677 Status
= EFI_SUCCESS
;
1678 FormattedString
= NULL
;
1679 OptionString
= NULL
;
1680 ScreenOperation
= UiNoOperation
;
1682 MinRefreshInterval
= 0;
1685 OutputString
= NULL
;
1690 MenuRefreshEntry
= gMenuRefreshHead
;
1692 NextMenuOption
= NULL
;
1693 PreviousMenuOption
= NULL
;
1694 SavedMenuOption
= NULL
;
1697 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1699 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
1700 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1701 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1703 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1704 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1707 Col
= LocalScreen
.LeftColumn
;
1708 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1710 Selection
->TopRow
= TopRow
;
1711 Selection
->BottomRow
= BottomRow
;
1712 Selection
->PromptCol
= Col
;
1713 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1714 Selection
->Statement
= NULL
;
1716 TopOfScreen
= gMenuOption
.ForwardLink
;
1721 // Find current Menu
1723 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
1724 if (CurrentMenu
== NULL
) {
1726 // Current menu not found, add it to the menu tree
1728 CurrentMenu
= UiAddMenuList (NULL
, &Selection
->FormSetGuid
, Selection
->FormId
);
1730 ASSERT (CurrentMenu
!= NULL
);
1732 if (Selection
->QuestionId
== 0) {
1734 // Highlight not specified, fetch it from cached menu
1736 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
1740 // Get user's selection
1742 NewPos
= gMenuOption
.ForwardLink
;
1744 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1745 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1747 ControlFlag
= CfInitialization
;
1748 Selection
->Action
= UI_ACTION_NONE
;
1750 switch (ControlFlag
) {
1751 case CfInitialization
:
1752 if (IsListEmpty (&gMenuOption
)) {
1753 ControlFlag
= CfReadKey
;
1755 ControlFlag
= CfCheckSelection
;
1759 case CfCheckSelection
:
1760 if (Selection
->Action
!= UI_ACTION_NONE
) {
1761 ControlFlag
= CfExit
;
1763 ControlFlag
= CfRepaint
;
1768 ControlFlag
= CfRefreshHighLight
;
1782 LocalScreen
.LeftColumn
,
1783 LocalScreen
.RightColumn
,
1784 TopRow
- SCROLL_ARROW_HEIGHT
,
1785 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1786 FIELD_TEXT
| FIELD_BACKGROUND
1789 UiFreeRefreshList ();
1790 MinRefreshInterval
= 0;
1792 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
1793 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1794 MenuOption
->Row
= Row
;
1795 MenuOption
->Col
= Col
;
1796 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1798 Statement
= MenuOption
->ThisTag
;
1799 if (Statement
->InSubtitle
) {
1800 MenuOption
->Col
+= SUBTITLE_INDENT
;
1803 if (MenuOption
->GrayOut
) {
1804 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1806 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1807 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1811 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1814 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1815 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1816 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1819 // If there is more string to process print on the next row and increment the Skip value
1821 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
1827 FreePool (OutputString
);
1836 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1837 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1838 if (EFI_ERROR (Status
)) {
1840 // Repaint to clear possible error prompt pop-up
1844 ControlFlag
= CfRepaint
;
1848 if (OptionString
!= NULL
) {
1849 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1851 // If leading spaces on OptionString - remove the spaces
1853 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1854 MenuOption
->OptCol
++;
1857 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1858 OptionString
[Count
] = OptionString
[Index
];
1862 OptionString
[Count
] = CHAR_NULL
;
1866 // If Question request refresh, register the op-code
1868 if (Statement
->RefreshInterval
!= 0) {
1870 // Menu will be refreshed at minimal interval of all Questions
1871 // which have refresh request
1873 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1874 MinRefreshInterval
= Statement
->RefreshInterval
;
1877 if (gMenuRefreshHead
== NULL
) {
1878 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1879 ASSERT (MenuRefreshEntry
!= NULL
);
1880 MenuRefreshEntry
->MenuOption
= MenuOption
;
1881 MenuRefreshEntry
->Selection
= Selection
;
1882 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1883 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1884 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1885 gMenuRefreshHead
= MenuRefreshEntry
;
1888 // Advance to the last entry
1890 for (MenuRefreshEntry
= gMenuRefreshHead
;
1891 MenuRefreshEntry
->Next
!= NULL
;
1892 MenuRefreshEntry
= MenuRefreshEntry
->Next
1895 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1896 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1897 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1898 MenuRefreshEntry
->MenuOption
= MenuOption
;
1899 MenuRefreshEntry
->Selection
= Selection
;
1900 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1901 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1902 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1906 Width
= (UINT16
) gOptionBlockWidth
;
1909 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1910 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1911 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1914 // If there is more string to process print on the next row and increment the Skip value
1916 if (StrLen (&OptionString
[Index
]) != 0) {
1920 // Since the Number of lines for this menu entry may or may not be reflected accurately
1921 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1922 // some testing to ensure we are keeping this in-sync.
1924 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1926 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1932 FreePool (OutputString
);
1941 FreePool (OptionString
);
1944 // If this is a text op with secondary text information
1946 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1947 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1949 Width
= (UINT16
) gOptionBlockWidth
;
1952 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1953 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1954 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1957 // If there is more string to process print on the next row and increment the Skip value
1959 if (StrLen (&StringPtr
[Index
]) != 0) {
1963 // Since the Number of lines for this menu entry may or may not be reflected accurately
1964 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1965 // some testing to ensure we are keeping this in-sync.
1967 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1969 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1975 FreePool (OutputString
);
1982 FreePool (StringPtr
);
1986 // Need to handle the bottom of the display
1988 if (MenuOption
->Skip
> 1) {
1989 Row
+= MenuOption
->Skip
- SkipValue
;
1992 Row
+= MenuOption
->Skip
;
1995 if (Row
> BottomRow
) {
1996 if (!ValueIsScroll (FALSE
, Link
)) {
2000 Row
= BottomRow
+ 1;
2005 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2010 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2012 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2013 TopRow
- SCROLL_ARROW_HEIGHT
,
2017 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2021 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2023 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2024 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2028 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2035 case CfRefreshHighLight
:
2037 // MenuOption: Last menu option that need to remove hilight
2038 // MenuOption is set to NULL in Repaint
2039 // NewPos: Current menu option that need to hilight
2041 ControlFlag
= CfUpdateHelpString
;
2044 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2045 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2047 SavedValue
= Repaint
;
2050 if (Selection
->QuestionId
!= 0) {
2051 NewPos
= gMenuOption
.ForwardLink
;
2052 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2054 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &gMenuOption
) {
2055 NewPos
= NewPos
->ForwardLink
;
2056 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2058 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2060 // Target Question found, find its MenuOption
2064 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2065 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2066 Index
+= SavedMenuOption
->Skip
;
2067 Link
= Link
->ForwardLink
;
2070 if (Link
!= NewPos
|| Index
> BottomRow
) {
2072 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2075 for (Index
= TopRow
; Index
<= BottomRow
; ) {
2076 Link
= Link
->BackLink
;
2077 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2078 Index
+= SavedMenuOption
->Skip
;
2080 TopOfScreen
= Link
->ForwardLink
;
2084 ControlFlag
= CfRepaint
;
2089 // Target Question not found, highlight the default menu option
2091 NewPos
= TopOfScreen
;
2094 Selection
->QuestionId
= 0;
2097 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2098 if (MenuOption
!= NULL
) {
2100 // Remove highlight on last Menu Option
2102 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2103 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2104 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2105 if (OptionString
!= NULL
) {
2106 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2107 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2110 // If leading spaces on OptionString - remove the spaces
2112 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2115 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2116 OptionString
[Count
] = OptionString
[Index
];
2120 OptionString
[Count
] = CHAR_NULL
;
2123 Width
= (UINT16
) gOptionBlockWidth
;
2124 OriginalRow
= MenuOption
->Row
;
2126 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2127 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2128 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2131 // If there is more string to process print on the next row and increment the Skip value
2133 if (StrLen (&OptionString
[Index
]) != 0) {
2137 FreePool (OutputString
);
2140 MenuOption
->Row
= OriginalRow
;
2142 FreePool (OptionString
);
2145 if (MenuOption
->GrayOut
) {
2146 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2147 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2148 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
2151 OriginalRow
= MenuOption
->Row
;
2152 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2154 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2155 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2156 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2159 // If there is more string to process print on the next row and increment the Skip value
2161 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2165 FreePool (OutputString
);
2168 MenuOption
->Row
= OriginalRow
;
2169 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2175 // This is only possible if we entered this page and the first menu option is
2176 // a "non-menu" item. In that case, force it UiDown
2178 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2179 if (!IsSelectable (MenuOption
)) {
2180 ASSERT (ScreenOperation
== UiNoOperation
);
2181 ScreenOperation
= UiDown
;
2182 ControlFlag
= CfScreenOperation
;
2187 // This is the current selected statement
2189 Statement
= MenuOption
->ThisTag
;
2190 Selection
->Statement
= Statement
;
2192 // Record highlight for current menu
2194 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2197 // Set reverse attribute
2199 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
2200 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2203 // Assuming that we have a refresh linked-list created, lets annotate the
2204 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2205 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2207 if (gMenuRefreshHead
!= NULL
) {
2208 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2209 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
2210 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2211 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
2216 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2217 if (OptionString
!= NULL
) {
2218 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2220 // If leading spaces on OptionString - remove the spaces
2222 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2225 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2226 OptionString
[Count
] = OptionString
[Index
];
2230 OptionString
[Count
] = CHAR_NULL
;
2232 Width
= (UINT16
) gOptionBlockWidth
;
2234 OriginalRow
= MenuOption
->Row
;
2236 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2237 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2238 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2241 // If there is more string to process print on the next row and increment the Skip value
2243 if (StrLen (&OptionString
[Index
]) != 0) {
2247 FreePool (OutputString
);
2250 MenuOption
->Row
= OriginalRow
;
2252 FreePool (OptionString
);
2255 OriginalRow
= MenuOption
->Row
;
2257 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2259 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2260 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2261 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2264 // If there is more string to process print on the next row and increment the Skip value
2266 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2270 FreePool (OutputString
);
2273 MenuOption
->Row
= OriginalRow
;
2278 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2281 // Clear reverse attribute
2283 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2286 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2287 // if we didn't break halfway when process CfRefreshHighLight.
2289 Repaint
= SavedValue
;
2292 case CfUpdateHelpString
:
2293 ControlFlag
= CfPrepareToReadKey
;
2295 if (Repaint
|| NewLine
) {
2297 // Don't print anything if it is a NULL help token
2299 ASSERT(MenuOption
!= NULL
);
2300 if (MenuOption
->ThisTag
->Help
== 0) {
2303 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2306 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2308 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2310 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2312 // Pad String with spaces to simulate a clearing of the previous line
2314 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2315 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2319 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2321 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2326 // Reset this flag every time we finish using it.
2332 case CfPrepareToReadKey
:
2333 ControlFlag
= CfReadKey
;
2334 ScreenOperation
= UiNoOperation
;
2338 ControlFlag
= CfScreenOperation
;
2341 // Wait for user's selection
2344 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2345 } while (Status
== EFI_TIMEOUT
);
2347 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2349 // IFR is updated in Callback of refresh opcode, re-parse it
2351 Selection
->Statement
= NULL
;
2355 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2357 // If we encounter error, continue to read another key in.
2359 if (EFI_ERROR (Status
)) {
2360 ControlFlag
= CfReadKey
;
2364 switch (Key
.UnicodeChar
) {
2365 case CHAR_CARRIAGE_RETURN
:
2366 ScreenOperation
= UiSelect
;
2371 // We will push the adjustment of these numeric values directly to the input handler
2372 // NOTE: we won't handle manual input numeric
2377 // If the screen has no menu items, and the user didn't select UiReset
2378 // ignore the selection and go back to reading keys.
2380 if(IsListEmpty (&gMenuOption
)) {
2381 ControlFlag
= CfReadKey
;
2385 ASSERT(MenuOption
!= NULL
);
2386 Statement
= MenuOption
->ThisTag
;
2387 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2388 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2389 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2391 if (Key
.UnicodeChar
== '+') {
2392 gDirection
= SCAN_RIGHT
;
2394 gDirection
= SCAN_LEFT
;
2396 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2397 if (EFI_ERROR (Status
)) {
2399 // Repaint to clear possible error prompt pop-up
2404 if (OptionString
!= NULL
) {
2405 FreePool (OptionString
);
2411 ScreenOperation
= UiUp
;
2416 ScreenOperation
= UiDown
;
2420 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2422 // If the screen has no menu items, and the user didn't select UiReset
2423 // ignore the selection and go back to reading keys.
2425 if(IsListEmpty (&gMenuOption
)) {
2426 ControlFlag
= CfReadKey
;
2430 ASSERT(MenuOption
!= NULL
);
2431 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2432 ScreenOperation
= UiSelect
;
2438 if (((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2439 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2442 // If the function key has been disabled, just ignore the key.
2445 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2446 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2447 if (Key
.ScanCode
== SCAN_F9
) {
2449 // Reset to standard default
2451 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2453 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2462 case CfScreenOperation
:
2463 if (ScreenOperation
!= UiReset
) {
2465 // If the screen has no menu items, and the user didn't select UiReset
2466 // ignore the selection and go back to reading keys.
2468 if (IsListEmpty (&gMenuOption
)) {
2469 ControlFlag
= CfReadKey
;
2473 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2475 for (Link
= gMenuOption
.ForwardLink
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2476 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2477 if (IsSelectable (NextMenuOption
)) {
2482 if (Link
== &gMenuOption
) {
2483 ControlFlag
= CfPrepareToReadKey
;
2489 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2492 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2493 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2500 ControlFlag
= CfCheckSelection
;
2502 ASSERT(MenuOption
!= NULL
);
2503 Statement
= MenuOption
->ThisTag
;
2504 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2505 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2506 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2507 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2512 // Keep highlight on current MenuOption
2514 Selection
->QuestionId
= Statement
->QuestionId
;
2516 switch (Statement
->Operand
) {
2517 case EFI_IFR_REF_OP
:
2518 if (Statement
->RefDevicePath
!= 0) {
2520 // Goto another Hii Package list
2522 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2524 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2525 if (StringPtr
== NULL
) {
2527 // No device path string not found, exit
2529 Selection
->Action
= UI_ACTION_EXIT
;
2530 Selection
->Statement
= NULL
;
2533 BufferSize
= StrLen (StringPtr
) / 2;
2534 DevicePath
= AllocatePool (BufferSize
);
2537 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2539 DevicePathBuffer
= (UINT8
*) DevicePath
;
2540 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
2541 TemStr
[0] = StringPtr
[Index
];
2542 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
2543 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
2545 // Invalid Hex Char as the tail.
2549 if ((Index
& 1) == 0) {
2550 DevicePathBuffer
[Index
/2] = DigitUint8
;
2552 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2556 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2557 if (Selection
->Handle
== NULL
) {
2559 // If target Hii Handle not found, exit
2561 Selection
->Action
= UI_ACTION_EXIT
;
2562 Selection
->Statement
= NULL
;
2566 FreePool (StringPtr
);
2567 FreePool (DevicePath
);
2569 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2570 Selection
->FormId
= Statement
->RefFormId
;
2571 Selection
->QuestionId
= Statement
->RefQuestionId
;
2572 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2574 // Goto another Formset, check for uncommitted data
2576 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2578 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2579 Selection
->FormId
= Statement
->RefFormId
;
2580 Selection
->QuestionId
= Statement
->RefQuestionId
;
2581 } else if (Statement
->RefFormId
!= 0) {
2583 // Check whether target From is suppressed.
2585 RefForm
= IdToForm (Selection
->FormSet
, Statement
->RefFormId
);
2587 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2588 Status
= EvaluateExpression (Selection
->FormSet
, RefForm
, RefForm
->SuppressExpression
);
2589 if (EFI_ERROR (Status
)) {
2593 if (RefForm
->SuppressExpression
->Result
.Value
.b
) {
2595 // Form is suppressed.
2598 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2599 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2607 // Goto another form inside this formset,
2609 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2612 // Link current form so that we can always go back when someone hits the ESC
2614 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Statement
->RefFormId
);
2615 if (MenuList
== NULL
) {
2616 MenuList
= UiAddMenuList (CurrentMenu
, &Selection
->FormSetGuid
, Statement
->RefFormId
);
2619 Selection
->FormId
= Statement
->RefFormId
;
2620 Selection
->QuestionId
= Statement
->RefQuestionId
;
2621 } else if (Statement
->RefQuestionId
!= 0) {
2623 // Goto another Question
2625 Selection
->QuestionId
= Statement
->RefQuestionId
;
2627 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2628 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2637 case EFI_IFR_ACTION_OP
:
2639 // Process the Config string <ConfigResp>
2641 Status
= ProcessQuestionConfig (Selection
, Statement
);
2643 if (EFI_ERROR (Status
)) {
2648 // The action button may change some Question value, so refresh the form
2650 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2653 case EFI_IFR_RESET_BUTTON_OP
:
2655 // Reset Question to default value specified by DefaultId
2657 ControlFlag
= CfUiDefault
;
2658 DefaultId
= Statement
->DefaultId
;
2663 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2665 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
2666 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2668 if (EFI_ERROR (Status
)) {
2671 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2673 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2676 if (OptionString
!= NULL
) {
2677 FreePool (OptionString
);
2685 // We come here when someone press ESC
2687 ControlFlag
= CfCheckSelection
;
2689 if (CurrentMenu
->Parent
!= NULL
) {
2691 // we have a parent, so go to the parent menu
2693 if (CompareGuid (&CurrentMenu
->FormSetGuid
, &CurrentMenu
->Parent
->FormSetGuid
)) {
2695 // The parent menu and current menu are in the same formset
2697 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2699 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2701 Selection
->Statement
= NULL
;
2703 Selection
->FormId
= CurrentMenu
->Parent
->FormId
;
2704 Selection
->QuestionId
= CurrentMenu
->Parent
->QuestionId
;
2707 // Clear highlight record for this menu
2709 CurrentMenu
->QuestionId
= 0;
2713 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
) {
2715 // We never exit FrontPage, so skip the ESC
2717 Selection
->Action
= UI_ACTION_NONE
;
2722 // We are going to leave current FormSet, so check uncommited data in this FormSet
2724 if (gNvUpdateRequired
) {
2725 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2727 YesResponse
= gYesResponse
[0];
2728 NoResponse
= gNoResponse
[0];
2731 // If NV flag is up, prompt user
2734 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveChanges
, gAreYouSure
, gEmptyString
);
2737 (Key
.ScanCode
!= SCAN_ESC
) &&
2738 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2739 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2742 if (Key
.ScanCode
== SCAN_ESC
) {
2744 // User hits the ESC key
2749 Selection
->Action
= UI_ACTION_NONE
;
2754 // If the user hits the YesResponse key
2756 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2757 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2761 Selection
->Action
= UI_ACTION_EXIT
;
2762 Selection
->Statement
= NULL
;
2763 CurrentMenu
->QuestionId
= 0;
2768 ControlFlag
= CfCheckSelection
;
2769 ASSERT(MenuOption
!= NULL
);
2770 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2771 if (MenuOption
->Sequence
!= 0) {
2773 // In the middle or tail of the Date/Time op-code set, go left.
2775 ASSERT(NewPos
!= NULL
);
2776 NewPos
= NewPos
->BackLink
;
2782 ControlFlag
= CfCheckSelection
;
2783 ASSERT(MenuOption
!= NULL
);
2784 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2785 if (MenuOption
->Sequence
!= 2) {
2787 // In the middle or tail of the Date/Time op-code set, go left.
2789 ASSERT(NewPos
!= NULL
);
2790 NewPos
= NewPos
->ForwardLink
;
2796 ControlFlag
= CfCheckSelection
;
2798 SavedListEntry
= TopOfScreen
;
2800 ASSERT(NewPos
!= NULL
);
2801 if (NewPos
->BackLink
!= &gMenuOption
) {
2804 // Adjust Date/Time position before we advance forward.
2806 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2809 // Caution that we have already rewind to the top, don't go backward in this situation.
2811 if (NewPos
->BackLink
!= &gMenuOption
) {
2812 NewPos
= NewPos
->BackLink
;
2815 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2816 DistanceValue
= PreviousMenuOption
->Skip
;
2819 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2820 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2821 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2822 // checking can be done.
2824 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2827 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2828 // don't worry about a redraw.
2830 ASSERT(MenuOption
!= NULL
);
2831 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2833 TopOfScreen
= NewPos
;
2836 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2837 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2838 DistanceValue
+= PreviousMenuOption
->Skip
;
2840 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2841 if (Difference
> 0) {
2843 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2845 TopOfScreen
= NewPos
;
2851 if (Difference
< 0) {
2853 // We want to goto previous MenuOption, but finally we go down.
2854 // it means that we hit the begining MenuOption that can be focused
2855 // so we simply scroll to the top
2857 if (SavedListEntry
!= gMenuOption
.ForwardLink
) {
2858 TopOfScreen
= gMenuOption
.ForwardLink
;
2864 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2866 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2868 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2870 SavedMenuOption
= MenuOption
;
2871 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2872 if (!IsSelectable (MenuOption
)) {
2874 // If we are at the end of the list and sitting on a text op, we need to more forward
2876 ScreenOperation
= UiDown
;
2877 ControlFlag
= CfScreenOperation
;
2881 MenuOption
= SavedMenuOption
;
2886 ControlFlag
= CfCheckSelection
;
2888 ASSERT(NewPos
!= NULL
);
2889 if (NewPos
->BackLink
== &gMenuOption
) {
2898 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2900 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
2901 Index
= Index
- PreviousMenuOption
->Skip
;
2902 Link
= Link
->BackLink
;
2903 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2907 Difference
= MoveToNextStatement (TRUE
, &Link
);
2908 if (Difference
> 0) {
2910 // The focus MenuOption is above the TopOfScreen
2913 } else if (Difference
< 0) {
2915 // This happens when there is no MenuOption can be focused from
2916 // Current MenuOption to the first MenuOption
2918 TopOfScreen
= gMenuOption
.ForwardLink
;
2920 Index
+= Difference
;
2921 if (Index
< TopRow
) {
2925 if (NewPos
== Link
) {
2933 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2934 // Don't do this when we are already in the first page.
2936 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2937 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2941 ControlFlag
= CfCheckSelection
;
2943 if (NewPos
->ForwardLink
== &gMenuOption
) {
2952 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2954 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
2955 Index
= Index
+ NextMenuOption
->Skip
;
2956 Link
= Link
->ForwardLink
;
2957 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2960 Index
+= MoveToNextStatement (FALSE
, &Link
);
2961 if (Index
> BottomRow
) {
2963 // There are more MenuOption needing scrolling
2968 if (NewPos
== Link
&& Index
<= BottomRow
) {
2970 // Finally we know that NewPos is the last MenuOption can be focused.
2979 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2980 // Don't do this when we are already in the last page.
2982 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2983 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2987 ControlFlag
= CfCheckSelection
;
2989 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2990 // to be one that progresses to the next set of op-codes, we need to advance to the last
2991 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2992 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2993 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2994 // the Date/Time op-code.
2996 SavedListEntry
= NewPos
;
2997 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2999 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3000 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3002 NewPos
= NewPos
->ForwardLink
;
3003 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3005 DistanceValue
+= NextMenuOption
->Skip
;
3006 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
3008 // An option might be multi-line, so we need to reflect that data in the overall skip value
3010 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
3012 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3013 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3014 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3015 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3021 // If we are going to scroll, update TopOfScreen
3023 if (Temp
> BottomRow
) {
3026 // Is the current top of screen a zero-advance op-code?
3027 // If so, keep moving forward till we hit a >0 advance op-code
3029 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3032 // If bottom op-code is more than one line or top op-code is more than one line
3034 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3036 // Is the bottom op-code greater than or equal in size to the top op-code?
3038 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3040 // Skip the top op-code
3042 TopOfScreen
= TopOfScreen
->ForwardLink
;
3043 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3045 OldSkipValue
= Difference
;
3047 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3050 // If we have a remainder, skip that many more op-codes until we drain the remainder
3053 Difference
>= (INTN
) SavedMenuOption
->Skip
;
3054 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
3057 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3059 TopOfScreen
= TopOfScreen
->ForwardLink
;
3060 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3061 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
3062 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
3065 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
3066 TopOfScreen
= TopOfScreen
->ForwardLink
;
3067 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3068 Difference
= SavedMenuOption
->Skip
- Difference
;
3074 // Since we will act on this op-code in the next routine, and increment the
3075 // SkipValue, set the skips to one less than what is required.
3077 SkipValue
= Difference
- 1;
3081 // Since we will act on this op-code in the next routine, and increment the
3082 // SkipValue, set the skips to one less than what is required.
3084 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3087 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3088 TopOfScreen
= TopOfScreen
->ForwardLink
;
3091 SkipValue
= OldSkipValue
;
3095 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3096 // Let's set a skip flag to smoothly scroll the top of the screen.
3098 if (SavedMenuOption
->Skip
> 1) {
3099 if (SavedMenuOption
== NextMenuOption
) {
3106 TopOfScreen
= TopOfScreen
->ForwardLink
;
3108 } while (SavedMenuOption
->Skip
== 0);
3111 OldSkipValue
= SkipValue
;
3114 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3116 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3119 SavedMenuOption
= MenuOption
;
3120 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3121 if (!IsSelectable (MenuOption
)) {
3123 // If we are at the end of the list and sitting on a text op, we need to more forward
3125 ScreenOperation
= UiUp
;
3126 ControlFlag
= CfScreenOperation
;
3130 MenuOption
= SavedMenuOption
;
3132 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3134 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3139 ControlFlag
= CfCheckSelection
;
3144 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
3146 if (!EFI_ERROR (Status
)) {
3147 ASSERT(MenuOption
!= NULL
);
3148 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3149 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3152 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3153 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3161 ControlFlag
= CfCheckSelection
;
3162 if (!Selection
->FormEditable
) {
3164 // This Form is not editable, ignore the F9 (reset to default)
3169 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
3171 if (!EFI_ERROR (Status
)) {
3172 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3173 Selection
->Statement
= NULL
;
3176 // Show NV update flag on status bar
3178 gNvUpdateRequired
= TRUE
;
3182 case CfUiNoOperation
:
3183 ControlFlag
= CfCheckSelection
;
3187 UiFreeRefreshList ();
3189 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3190 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3191 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3192 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");