2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2009, 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.
19 LIST_ENTRY gMenuList
= INITIALIZE_LIST_HEAD_VARIABLE (gMenuList
);
20 MENU_REFRESH_ENTRY
*gMenuRefreshHead
;
23 // Search table for UiDisplayMenu()
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
64 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
112 BOOLEAN GetLineByWidthFinished
= FALSE
;
116 Set Buffer to Value for Size bytes.
118 @param Buffer Memory to set.
119 @param Size Number of bytes to set
120 @param Value Value of the set operation.
133 while ((Size
--) != 0) {
140 Initialize Menu option list.
148 InitializeListHead (&Menu
);
153 Free Menu option linked list.
161 UI_MENU_OPTION
*MenuOption
;
163 while (!IsListEmpty (&Menu
)) {
164 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
165 RemoveEntryList (&MenuOption
->Link
);
168 // We allocated space for this description when we did a GetToken, free it here
170 if (MenuOption
->Skip
!= 0) {
172 // For date/time, MenuOption->Description is shared by three Menu Options
173 // Data format : [01/02/2004] [11:22:33]
174 // Line number : 0 0 1 0 0 1
176 FreePool (MenuOption
->Description
);
178 FreePool (MenuOption
);
184 Create a menu with specified formset GUID and form ID, and add it as a child
185 of the given parent menu.
187 @param Parent The parent of menu to be added.
188 @param FormSetGuid The Formset Guid of menu to be added.
189 @param FormId The Form ID of menu to be added.
191 @return A pointer to the newly added menu or NULL if memory is insufficient.
196 IN OUT UI_MENU_LIST
*Parent
,
197 IN EFI_GUID
*FormSetGuid
,
201 UI_MENU_LIST
*MenuList
;
203 MenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
204 if (MenuList
== NULL
) {
208 MenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
209 InitializeListHead (&MenuList
->ChildListHead
);
211 CopyMem (&MenuList
->FormSetGuid
, FormSetGuid
, sizeof (EFI_GUID
));
212 MenuList
->FormId
= FormId
;
213 MenuList
->Parent
= Parent
;
215 if (Parent
== NULL
) {
217 // If parent is not specified, it is the root Form of a Formset
219 InsertTailList (&gMenuList
, &MenuList
->Link
);
221 InsertTailList (&Parent
->ChildListHead
, &MenuList
->Link
);
229 Search Menu with given FormId in the parent menu and all its child menus.
231 @param Parent The parent of menu to search.
232 @param FormId The Form ID of menu to search.
234 @return A pointer to menu found or NULL if not found.
238 UiFindChildMenuList (
239 IN UI_MENU_LIST
*Parent
,
245 UI_MENU_LIST
*MenuList
;
247 if (Parent
->FormId
== FormId
) {
251 Link
= GetFirstNode (&Parent
->ChildListHead
);
252 while (!IsNull (&Parent
->ChildListHead
, Link
)) {
253 Child
= UI_MENU_LIST_FROM_LINK (Link
);
255 MenuList
= UiFindChildMenuList (Child
, FormId
);
256 if (MenuList
!= NULL
) {
260 Link
= GetNextNode (&Parent
->ChildListHead
, Link
);
268 Search Menu with given FormSetGuid and FormId in all cached menu list.
270 @param FormSetGuid The Formset GUID of the menu to search.
271 @param FormId The Form ID of menu to search.
273 @return A pointer to menu found or NULL if not found.
278 IN EFI_GUID
*FormSetGuid
,
283 UI_MENU_LIST
*MenuList
;
286 Link
= GetFirstNode (&gMenuList
);
287 while (!IsNull (&gMenuList
, Link
)) {
288 MenuList
= UI_MENU_LIST_FROM_LINK (Link
);
290 if (CompareGuid (FormSetGuid
, &MenuList
->FormSetGuid
)) {
292 // This is the formset we are looking for, find the form in this formset
294 Child
= UiFindChildMenuList (MenuList
, FormId
);
300 Link
= GetNextNode (&gMenuList
, Link
);
308 Free Menu option linked list.
316 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
318 while (gMenuRefreshHead
!= NULL
) {
319 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
320 FreePool (gMenuRefreshHead
);
321 gMenuRefreshHead
= OldMenuRefreshEntry
;
324 gMenuRefreshHead
= NULL
;
338 CHAR16
*OptionString
;
339 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
342 UI_MENU_SELECTION
*Selection
;
343 FORM_BROWSER_STATEMENT
*Question
;
344 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
345 EFI_HII_VALUE
*HiiValue
;
346 EFI_BROWSER_ACTION_REQUEST ActionRequest
;
348 if (gMenuRefreshHead
!= NULL
) {
350 MenuRefreshEntry
= gMenuRefreshHead
;
353 // Reset FormPackage update flag
355 mHiiPackageListUpdated
= FALSE
;
358 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
360 Selection
= MenuRefreshEntry
->Selection
;
361 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
363 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
364 if (EFI_ERROR (Status
)) {
369 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
371 if (OptionString
!= NULL
) {
373 // If leading spaces on OptionString - remove the spaces
375 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
378 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, &OptionString
[Index
]);
379 FreePool (OptionString
);
383 // Question value may be changed, need invoke its Callback()
385 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
386 if (((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) && (ConfigAccess
!= NULL
)) {
387 ActionRequest
= EFI_BROWSER_ACTION_REQUEST_NONE
;
389 HiiValue
= &Question
->HiiValue
;
390 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
392 // Create String in HII database for Configuration Driver to retrieve
394 HiiValue
->Value
.string
= NewString ((CHAR16
*) Question
->BufferValue
, Selection
->FormSet
->HiiHandle
);
397 Status
= ConfigAccess
->Callback (
399 EFI_BROWSER_ACTION_CHANGING
,
400 Question
->QuestionId
,
406 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
408 // Clean the String in HII Database
410 DeleteString (HiiValue
->Value
.string
, Selection
->FormSet
->HiiHandle
);
413 if (!EFI_ERROR (Status
)) {
414 switch (ActionRequest
) {
415 case EFI_BROWSER_ACTION_REQUEST_RESET
:
416 gResetRequired
= TRUE
;
419 case EFI_BROWSER_ACTION_REQUEST_SUBMIT
:
420 SubmitForm (Selection
->FormSet
, Selection
->Form
);
423 case EFI_BROWSER_ACTION_REQUEST_EXIT
:
424 Selection
->Action
= UI_ACTION_EXIT
;
425 gNvUpdateRequired
= FALSE
;
434 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
436 } while (MenuRefreshEntry
!= NULL
);
438 if (mHiiPackageListUpdated
) {
440 // Package list is updated, force to reparse IFR binary of target Formset
442 mHiiPackageListUpdated
= FALSE
;
443 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
453 Wait for a given event to fire, or for an optional timeout to expire.
455 @param Event The event to wait for
456 @param Timeout An optional timeout value in 100 ns units.
457 @param RefreshInterval Menu refresh interval (in seconds).
459 @retval EFI_SUCCESS Event fired before Timeout expired.
460 @retval EFI_TIME_OUT Timout expired before Event fired.
464 UiWaitForSingleEvent (
466 IN UINT64 Timeout
, OPTIONAL
467 IN UINT8 RefreshInterval OPTIONAL
472 EFI_EVENT TimerEvent
;
473 EFI_EVENT WaitList
[2];
477 // Create a timer event
479 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
480 if (!EFI_ERROR (Status
)) {
482 // Set the timer event
491 // Wait for the original event or the timer
494 WaitList
[1] = TimerEvent
;
495 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
496 gBS
->CloseEvent (TimerEvent
);
499 // If the timer expired, change the return to timed out
501 if (!EFI_ERROR (Status
) && Index
== 1) {
502 Status
= EFI_TIMEOUT
;
507 // Update screen every second
509 if (RefreshInterval
== 0) {
510 Timeout
= ONE_SECOND
;
512 Timeout
= RefreshInterval
* ONE_SECOND
;
516 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
519 // Set the timer event
528 // Wait for the original event or the timer
531 WaitList
[1] = TimerEvent
;
532 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
535 // If the timer expired, update anything that needs a refresh and keep waiting
537 if (!EFI_ERROR (Status
) && Index
== 1) {
538 Status
= EFI_TIMEOUT
;
539 if (RefreshInterval
!= 0) {
540 Status
= RefreshForm ();
544 gBS
->CloseEvent (TimerEvent
);
545 } while (Status
== EFI_TIMEOUT
);
553 Add one menu option by specified description and context.
555 @param String String description for this option.
556 @param Handle Hii handle for the package list.
557 @param Statement Statement of this Menu Option.
558 @param NumberOfLines Display lines for this Menu Option.
559 @param MenuItemCount The index for this Option in the Menu.
561 @retval Pointer Pointer to the added Menu Option.
567 IN EFI_HII_HANDLE Handle
,
568 IN FORM_BROWSER_STATEMENT
*Statement
,
569 IN UINT16 NumberOfLines
,
570 IN UINT16 MenuItemCount
573 UI_MENU_OPTION
*MenuOption
;
580 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
582 // Add three MenuOptions for Date/Time
583 // Data format : [01/02/2004] [11:22:33]
584 // Line number : 0 0 1 0 0 1
589 if (Statement
->Storage
== NULL
) {
591 // For RTC type of date/time, set default refresh interval to be 1 second
593 if (Statement
->RefreshInterval
== 0) {
594 Statement
->RefreshInterval
= 1;
599 for (Index
= 0; Index
< Count
; Index
++) {
600 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
603 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
604 MenuOption
->Description
= String
;
605 MenuOption
->Handle
= Handle
;
606 MenuOption
->ThisTag
= Statement
;
607 MenuOption
->EntryNumber
= MenuItemCount
;
611 // Override LineNumber for the MenuOption in Date/Time sequence
613 MenuOption
->Skip
= 1;
615 MenuOption
->Skip
= NumberOfLines
;
617 MenuOption
->Sequence
= Index
;
619 if (Statement
->GrayOutExpression
!= NULL
) {
620 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
623 switch (Statement
->Operand
) {
624 case EFI_IFR_ORDERED_LIST_OP
:
625 case EFI_IFR_ONE_OF_OP
:
626 case EFI_IFR_NUMERIC_OP
:
627 case EFI_IFR_TIME_OP
:
628 case EFI_IFR_DATE_OP
:
629 case EFI_IFR_CHECKBOX_OP
:
630 case EFI_IFR_PASSWORD_OP
:
631 case EFI_IFR_STRING_OP
:
633 // User could change the value of these items
635 MenuOption
->IsQuestion
= TRUE
;
639 MenuOption
->IsQuestion
= FALSE
;
643 if ((Statement
->ValueExpression
!= NULL
) ||
644 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
645 MenuOption
->ReadOnly
= TRUE
;
648 InsertTailList (&Menu
, &MenuOption
->Link
);
656 Routine used to abstract a generic dialog interface and return the selected key or string
658 @param NumberOfLines The number of lines for the dialog box
659 @param HotKey Defines whether a single character is parsed
660 (TRUE) and returned in KeyValue or a string is
661 returned in StringBuffer. Two special characters
662 are considered when entering a string, a SCAN_ESC
663 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
664 string input and returns
665 @param MaximumStringSize The maximum size in bytes of a typed in string
666 (each character is a CHAR16) and the minimum
667 string returned is two bytes
668 @param StringBuffer The passed in pointer to the buffer which will
669 hold the typed in string if HotKey is FALSE
670 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
671 @param ... A series of (quantity == NumberOfLines) text
672 strings which will be used to construct the dialog
675 @retval EFI_SUCCESS Displayed dialog and received user interaction
676 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
677 (StringBuffer == NULL) && (HotKey == FALSE))
678 @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.
982 CreateMultiStringPopUp (
983 IN UINTN RequestedWidth
,
984 IN UINTN NumberOfLines
,
990 VA_START (Marker
, NumberOfLines
);
992 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
999 Update status bar on the bottom of menu.
1001 @param MessageType The type of message to be shown.
1002 @param Flags The flags in Question header.
1003 @param State Set or clear.
1008 IN UINTN MessageType
,
1014 CHAR16
*NvUpdateMessage
;
1015 CHAR16
*InputErrorMessage
;
1017 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1018 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1020 switch (MessageType
) {
1023 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1025 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1026 gScreenDimensions
.BottomRow
- 1,
1031 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1032 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1033 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1036 mInputError
= FALSE
;
1040 case NV_UPDATE_REQUIRED
:
1041 if (gClassOfVfr
!= FORMSET_CLASS_FRONT_PAGE
) {
1043 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1045 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1046 gScreenDimensions
.BottomRow
- 1,
1049 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1051 gNvUpdateRequired
= TRUE
;
1053 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
1054 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1056 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1057 gScreenDimensions
.BottomRow
- 1,
1062 gNvUpdateRequired
= FALSE
;
1067 case REFRESH_STATUS_BAR
:
1069 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
1072 if (gNvUpdateRequired
) {
1073 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1081 FreePool (InputErrorMessage
);
1082 FreePool (NvUpdateMessage
);
1088 Get the supported width for a particular op-code
1090 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1091 @param Handle The handle in the HII database being used
1093 @return Returns the number of CHAR16 characters that is support.
1098 IN FORM_BROWSER_STATEMENT
*Statement
,
1099 IN EFI_HII_HANDLE Handle
1109 // See if the second text parameter is really NULL
1111 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1112 String
= GetToken (Statement
->TextTwo
, Handle
);
1113 Size
= StrLen (String
);
1117 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1118 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1119 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1120 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1121 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1123 // Allow a wide display if text op-code and no secondary text op-code
1125 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1127 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1129 Width
= (UINT16
) gPromptBlockWidth
;
1132 if (Statement
->InSubtitle
) {
1133 Width
-= SUBTITLE_INDENT
;
1140 Will copy LineWidth amount of a string in the OutputString buffer and return the
1141 number of CHAR16 characters that were copied into the OutputString buffer.
1143 @param InputString String description for this option.
1144 @param LineWidth Width of the desired string to extract in CHAR16
1146 @param Index Where in InputString to start the copy process
1147 @param OutputString Buffer to copy the string into
1149 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1154 IN CHAR16
*InputString
,
1155 IN UINT16 LineWidth
,
1156 IN OUT UINTN
*Index
,
1157 OUT CHAR16
**OutputString
1163 if (GetLineByWidthFinished
) {
1164 GetLineByWidthFinished
= FALSE
;
1171 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1174 // Ensure we have got a valid buffer
1176 if (*OutputString
!= NULL
) {
1179 //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.
1180 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1182 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1183 *Index
= *Index
+ 2;
1187 // Fast-forward the string and see if there is a carriage-return in the string
1189 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1193 // Copy the desired LineWidth of data to the output buffer.
1194 // Also make sure that we don't copy more than the string.
1195 // Also make sure that if there are linefeeds, we account for them.
1197 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1198 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1201 // Convert to CHAR16 value and show that we are done with this operation
1203 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1204 if (LineWidth
!= 0) {
1205 GetLineByWidthFinished
= TRUE
;
1208 if (Count2
== LineWidth
) {
1210 // Rewind the string from the maximum size until we see a space to break the line
1212 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1214 if (LineWidth
== 0) {
1222 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1225 // If currently pointing to a space, increment the index to the first non-space character
1228 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1232 *Index
= (UINT16
) (*Index
+ LineWidth
);
1241 Update display lines for a Menu Option.
1243 @param Selection The user's selection.
1244 @param MenuOption The MenuOption to be checked.
1245 @param OptionalString The option string.
1246 @param SkipValue The number of lins to skip.
1250 UpdateOptionSkipLines (
1251 IN UI_MENU_SELECTION
*Selection
,
1252 IN UI_MENU_OPTION
*MenuOption
,
1253 OUT CHAR16
**OptionalString
,
1261 CHAR16
*OutputString
;
1262 CHAR16
*OptionString
;
1265 OptionString
= *OptionalString
;
1266 OutputString
= NULL
;
1268 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1270 if (OptionString
!= NULL
) {
1271 Width
= (UINT16
) gOptionBlockWidth
;
1275 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1277 // If there is more string to process print on the next row and increment the Skip value
1279 if (StrLen (&OptionString
[Index
]) != 0) {
1280 if (SkipValue
== 0) {
1283 // Since the Number of lines for this menu entry may or may not be reflected accurately
1284 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1285 // some testing to ensure we are keeping this in-sync.
1287 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1289 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1295 FreePool (OutputString
);
1296 if (SkipValue
!= 0) {
1304 *OptionalString
= OptionString
;
1309 Check whether this Menu Option could be highlighted.
1311 This is an internal function.
1313 @param MenuOption The MenuOption to be checked.
1315 @retval TRUE This Menu Option is selectable.
1316 @retval FALSE This Menu Option could not be selected.
1321 UI_MENU_OPTION
*MenuOption
1324 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1325 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1334 Determine if the menu is the last menu that can be selected.
1336 This is an internal function.
1338 @param Direction The scroll direction. False is down. True is up.
1339 @param CurrentPos The current focus.
1341 @return FALSE -- the menu isn't the last menu that can be selected.
1342 @return TRUE -- the menu is the last menu that can be selected.
1347 IN BOOLEAN Direction
,
1348 IN LIST_ENTRY
*CurrentPos
1352 UI_MENU_OPTION
*MenuOption
;
1354 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1356 if (Temp
== &Menu
) {
1360 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1361 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1362 if (IsSelectable (MenuOption
)) {
1372 Move to next selectable statement.
1374 This is an internal function.
1376 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1377 @param CurrentPosition Current position.
1379 @return The row distance from current MenuOption to next selectable MenuOption.
1383 MoveToNextStatement (
1385 IN OUT LIST_ENTRY
**CurrentPosition
1391 UI_MENU_OPTION
*NextMenuOption
;
1394 Pos
= *CurrentPosition
;
1398 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1399 if (IsSelectable (NextMenuOption
)) {
1402 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1406 Distance
+= NextMenuOption
->Skip
;
1407 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1412 // If we hit end there is still no statement can be focused,
1413 // we go backwards to find the statement can be focused.
1416 Pos
= *CurrentPosition
;
1419 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1420 if (IsSelectable (NextMenuOption
)) {
1423 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1427 Distance
-= NextMenuOption
->Skip
;
1428 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1432 *CurrentPosition
= &NextMenuOption
->Link
;
1438 Adjust Data and Time position accordingly.
1439 Data format : [01/02/2004] [11:22:33]
1440 Line number : 0 0 1 0 0 1
1442 This is an internal function.
1444 @param DirectionUp the up or down direction. False is down. True is
1446 @param CurrentPosition Current position. On return: Point to the last
1447 Option (Year or Second) if up; Point to the first
1448 Option (Month or Hour) if down.
1450 @return Return line number to pad. It is possible that we stand on a zero-advance
1451 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1455 AdjustDateAndTimePosition (
1456 IN BOOLEAN DirectionUp
,
1457 IN OUT LIST_ENTRY
**CurrentPosition
1461 LIST_ENTRY
*NewPosition
;
1462 UI_MENU_OPTION
*MenuOption
;
1463 UINTN PadLineNumber
;
1466 NewPosition
= *CurrentPosition
;
1467 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1469 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1470 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1472 // Calculate the distance from current position to the last Date/Time MenuOption
1475 while (MenuOption
->Skip
== 0) {
1477 NewPosition
= NewPosition
->ForwardLink
;
1478 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1482 NewPosition
= *CurrentPosition
;
1485 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1486 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1487 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1488 // checking can be done.
1490 while (Count
++ < 2) {
1491 NewPosition
= NewPosition
->BackLink
;
1495 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1496 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1497 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1498 // checking can be done.
1500 while (Count
-- > 0) {
1501 NewPosition
= NewPosition
->ForwardLink
;
1505 *CurrentPosition
= NewPosition
;
1508 return PadLineNumber
;
1512 Find HII Handle in the HII database associated with given Device Path.
1514 If DevicePath is NULL, then ASSERT.
1516 @param DevicePath Device Path associated with the HII package list
1519 @retval Handle HII package list Handle associated with the Device
1521 @retval NULL Hii Package list handle is not found.
1526 DevicePathToHiiHandle (
1527 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1531 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1536 EFI_HANDLE DriverHandle
;
1537 EFI_HII_HANDLE
*HiiHandles
;
1538 EFI_HII_HANDLE HiiHandle
;
1540 ASSERT (DevicePath
!= NULL
);
1542 TmpDevicePath
= DevicePath
;
1544 // Locate Device Path Protocol handle buffer
1546 Status
= gBS
->LocateDevicePath (
1547 &gEfiDevicePathProtocolGuid
,
1551 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1556 // Retrieve all HII Handles from HII database
1558 BufferSize
= 0x1000;
1559 HiiHandles
= AllocatePool (BufferSize
);
1560 ASSERT (HiiHandles
!= NULL
);
1561 Status
= mHiiDatabase
->ListPackageLists (
1563 EFI_HII_PACKAGE_TYPE_ALL
,
1568 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1569 FreePool (HiiHandles
);
1570 HiiHandles
= AllocatePool (BufferSize
);
1571 ASSERT (HiiHandles
!= NULL
);
1573 Status
= mHiiDatabase
->ListPackageLists (
1575 EFI_HII_PACKAGE_TYPE_ALL
,
1582 if (EFI_ERROR (Status
)) {
1583 FreePool (HiiHandles
);
1588 // Search Hii Handle by Driver Handle
1591 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1592 for (Index
= 0; Index
< HandleCount
; Index
++) {
1593 Status
= mHiiDatabase
->GetPackageListHandle (
1598 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1599 HiiHandle
= HiiHandles
[Index
];
1604 FreePool (HiiHandles
);
1609 Display menu and wait for user to select one menu option, then return it.
1610 If AutoBoot is enabled, then if user doesn't select any option,
1611 after period of time, it will automatically return the first menu option.
1613 @param Selection Menu selection.
1615 @retval EFI_SUCESSS This function always return successfully for now.
1620 IN OUT UI_MENU_SELECTION
*Selection
1626 UINTN DistanceValue
;
1638 CHAR16
*OptionString
;
1639 CHAR16
*OutputString
;
1640 CHAR16
*FormattedString
;
1650 LIST_ENTRY
*TopOfScreen
;
1651 LIST_ENTRY
*SavedListEntry
;
1652 UI_MENU_OPTION
*MenuOption
;
1653 UI_MENU_OPTION
*NextMenuOption
;
1654 UI_MENU_OPTION
*SavedMenuOption
;
1655 UI_MENU_OPTION
*PreviousMenuOption
;
1656 UI_CONTROL_FLAG ControlFlag
;
1657 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1658 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1659 UI_SCREEN_OPERATION ScreenOperation
;
1660 UINT8 MinRefreshInterval
;
1663 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1664 FORM_BROWSER_STATEMENT
*Statement
;
1666 UINT8
*DevicePathBuffer
;
1668 UI_MENU_LIST
*CurrentMenu
;
1669 UI_MENU_LIST
*MenuList
;
1671 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1673 Status
= EFI_SUCCESS
;
1674 FormattedString
= NULL
;
1675 OptionString
= NULL
;
1676 ScreenOperation
= UiNoOperation
;
1678 MinRefreshInterval
= 0;
1681 OutputString
= NULL
;
1686 MenuRefreshEntry
= gMenuRefreshHead
;
1688 NextMenuOption
= NULL
;
1689 PreviousMenuOption
= NULL
;
1690 SavedMenuOption
= NULL
;
1692 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1694 if (gClassOfVfr
== FORMSET_CLASS_FRONT_PAGE
) {
1695 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1696 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1698 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1699 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1702 Col
= LocalScreen
.LeftColumn
;
1703 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1705 Selection
->TopRow
= TopRow
;
1706 Selection
->BottomRow
= BottomRow
;
1707 Selection
->PromptCol
= Col
;
1708 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1709 Selection
->Statement
= NULL
;
1711 TopOfScreen
= Menu
.ForwardLink
;
1716 // Find current Menu
1718 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
1719 if (CurrentMenu
== NULL
) {
1721 // Current menu not found, add it to the menu tree
1723 CurrentMenu
= UiAddMenuList (NULL
, &Selection
->FormSetGuid
, Selection
->FormId
);
1725 ASSERT (CurrentMenu
!= NULL
);
1727 if (Selection
->QuestionId
== 0) {
1729 // Highlight not specified, fetch it from cached menu
1731 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
1735 // Get user's selection
1737 NewPos
= Menu
.ForwardLink
;
1739 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1740 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1742 ControlFlag
= CfInitialization
;
1743 Selection
->Action
= UI_ACTION_NONE
;
1745 switch (ControlFlag
) {
1746 case CfInitialization
:
1747 if (IsListEmpty (&Menu
)) {
1748 ControlFlag
= CfReadKey
;
1750 ControlFlag
= CfCheckSelection
;
1754 case CfCheckSelection
:
1755 if (Selection
->Action
!= UI_ACTION_NONE
) {
1756 ControlFlag
= CfExit
;
1758 ControlFlag
= CfRepaint
;
1763 ControlFlag
= CfRefreshHighLight
;
1777 LocalScreen
.LeftColumn
,
1778 LocalScreen
.RightColumn
,
1779 TopRow
- SCROLL_ARROW_HEIGHT
,
1780 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1781 FIELD_TEXT
| FIELD_BACKGROUND
1784 UiFreeRefreshList ();
1785 MinRefreshInterval
= 0;
1787 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1788 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1789 MenuOption
->Row
= Row
;
1790 MenuOption
->Col
= Col
;
1791 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1793 Statement
= MenuOption
->ThisTag
;
1794 if (Statement
->InSubtitle
) {
1795 MenuOption
->Col
+= SUBTITLE_INDENT
;
1798 if (MenuOption
->GrayOut
) {
1799 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1801 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1802 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1806 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1809 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1810 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1811 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1814 // If there is more string to process print on the next row and increment the Skip value
1816 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
1822 FreePool (OutputString
);
1831 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1832 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1833 if (EFI_ERROR (Status
)) {
1835 // Repaint to clear possible error prompt pop-up
1839 ControlFlag
= CfRepaint
;
1843 if (OptionString
!= NULL
) {
1844 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1846 // If leading spaces on OptionString - remove the spaces
1848 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1849 MenuOption
->OptCol
++;
1852 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1853 OptionString
[Count
] = OptionString
[Index
];
1857 OptionString
[Count
] = CHAR_NULL
;
1861 // If Question request refresh, register the op-code
1863 if (Statement
->RefreshInterval
!= 0) {
1865 // Menu will be refreshed at minimal interval of all Questions
1866 // which have refresh request
1868 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1869 MinRefreshInterval
= Statement
->RefreshInterval
;
1872 if (gMenuRefreshHead
== NULL
) {
1873 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1874 ASSERT (MenuRefreshEntry
!= NULL
);
1875 MenuRefreshEntry
->MenuOption
= MenuOption
;
1876 MenuRefreshEntry
->Selection
= Selection
;
1877 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1878 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1879 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1880 gMenuRefreshHead
= MenuRefreshEntry
;
1883 // Advance to the last entry
1885 for (MenuRefreshEntry
= gMenuRefreshHead
;
1886 MenuRefreshEntry
->Next
!= NULL
;
1887 MenuRefreshEntry
= MenuRefreshEntry
->Next
1890 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1891 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1892 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1893 MenuRefreshEntry
->MenuOption
= MenuOption
;
1894 MenuRefreshEntry
->Selection
= Selection
;
1895 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1896 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1897 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1901 Width
= (UINT16
) gOptionBlockWidth
;
1904 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1905 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1906 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1909 // If there is more string to process print on the next row and increment the Skip value
1911 if (StrLen (&OptionString
[Index
]) != 0) {
1915 // Since the Number of lines for this menu entry may or may not be reflected accurately
1916 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1917 // some testing to ensure we are keeping this in-sync.
1919 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1921 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1927 FreePool (OutputString
);
1936 FreePool (OptionString
);
1939 // If this is a text op with secondary text information
1941 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1942 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1944 Width
= (UINT16
) gOptionBlockWidth
;
1947 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1948 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1949 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1952 // If there is more string to process print on the next row and increment the Skip value
1954 if (StrLen (&StringPtr
[Index
]) != 0) {
1958 // Since the Number of lines for this menu entry may or may not be reflected accurately
1959 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1960 // some testing to ensure we are keeping this in-sync.
1962 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1964 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1970 FreePool (OutputString
);
1977 FreePool (StringPtr
);
1981 // Need to handle the bottom of the display
1983 if (MenuOption
->Skip
> 1) {
1984 Row
+= MenuOption
->Skip
- SkipValue
;
1987 Row
+= MenuOption
->Skip
;
1990 if (Row
> BottomRow
) {
1991 if (!ValueIsScroll (FALSE
, Link
)) {
1995 Row
= BottomRow
+ 1;
2000 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2005 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2007 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2008 TopRow
- SCROLL_ARROW_HEIGHT
,
2012 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2016 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2018 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2019 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2023 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2030 case CfRefreshHighLight
:
2032 // MenuOption: Last menu option that need to remove hilight
2033 // MenuOption is set to NULL in Repaint
2034 // NewPos: Current menu option that need to hilight
2036 ControlFlag
= CfUpdateHelpString
;
2039 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2040 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2042 SavedValue
= Repaint
;
2045 if (Selection
->QuestionId
!= 0) {
2046 NewPos
= Menu
.ForwardLink
;
2047 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2049 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
2050 NewPos
= NewPos
->ForwardLink
;
2051 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2053 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2055 // Target Question found, find its MenuOption
2059 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2060 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2061 Index
+= SavedMenuOption
->Skip
;
2062 Link
= Link
->ForwardLink
;
2065 if (Link
!= NewPos
|| Index
> BottomRow
) {
2067 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2070 for (Index
= TopRow
; Index
<= BottomRow
; ) {
2071 Link
= Link
->BackLink
;
2072 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2073 Index
+= SavedMenuOption
->Skip
;
2075 TopOfScreen
= Link
->ForwardLink
;
2079 ControlFlag
= CfRepaint
;
2084 // Target Question not found, highlight the default menu option
2086 NewPos
= TopOfScreen
;
2089 Selection
->QuestionId
= 0;
2092 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2093 if (MenuOption
!= NULL
) {
2095 // Remove highlight on last Menu Option
2097 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2098 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2099 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2100 if (OptionString
!= NULL
) {
2101 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2102 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2105 // If leading spaces on OptionString - remove the spaces
2107 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2110 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2111 OptionString
[Count
] = OptionString
[Index
];
2115 OptionString
[Count
] = CHAR_NULL
;
2118 Width
= (UINT16
) gOptionBlockWidth
;
2119 OriginalRow
= MenuOption
->Row
;
2121 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2122 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2123 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2126 // If there is more string to process print on the next row and increment the Skip value
2128 if (StrLen (&OptionString
[Index
]) != 0) {
2132 FreePool (OutputString
);
2135 MenuOption
->Row
= OriginalRow
;
2137 FreePool (OptionString
);
2140 if (MenuOption
->GrayOut
) {
2141 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2142 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2143 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
2146 OriginalRow
= MenuOption
->Row
;
2147 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2149 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2150 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2151 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2154 // If there is more string to process print on the next row and increment the Skip value
2156 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2160 FreePool (OutputString
);
2163 MenuOption
->Row
= OriginalRow
;
2164 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2170 // This is only possible if we entered this page and the first menu option is
2171 // a "non-menu" item. In that case, force it UiDown
2173 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2174 if (!IsSelectable (MenuOption
)) {
2175 ASSERT (ScreenOperation
== UiNoOperation
);
2176 ScreenOperation
= UiDown
;
2177 ControlFlag
= CfScreenOperation
;
2182 // This is the current selected statement
2184 Statement
= MenuOption
->ThisTag
;
2185 Selection
->Statement
= Statement
;
2187 // Record highlight for current menu
2189 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2192 // Set reverse attribute
2194 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
2195 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2198 // Assuming that we have a refresh linked-list created, lets annotate the
2199 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2200 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2202 if (gMenuRefreshHead
!= NULL
) {
2203 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2204 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
2205 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2206 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
2211 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2212 if (OptionString
!= NULL
) {
2213 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2215 // If leading spaces on OptionString - remove the spaces
2217 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2220 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2221 OptionString
[Count
] = OptionString
[Index
];
2225 OptionString
[Count
] = CHAR_NULL
;
2227 Width
= (UINT16
) gOptionBlockWidth
;
2229 OriginalRow
= MenuOption
->Row
;
2231 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2232 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2233 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2236 // If there is more string to process print on the next row and increment the Skip value
2238 if (StrLen (&OptionString
[Index
]) != 0) {
2242 FreePool (OutputString
);
2245 MenuOption
->Row
= OriginalRow
;
2247 FreePool (OptionString
);
2250 OriginalRow
= MenuOption
->Row
;
2252 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2254 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2255 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2256 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2259 // If there is more string to process print on the next row and increment the Skip value
2261 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2265 FreePool (OutputString
);
2268 MenuOption
->Row
= OriginalRow
;
2273 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2276 // Clear reverse attribute
2278 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2281 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2282 // if we didn't break halfway when process CfRefreshHighLight.
2284 Repaint
= SavedValue
;
2287 case CfUpdateHelpString
:
2288 ControlFlag
= CfPrepareToReadKey
;
2290 if (Repaint
|| NewLine
) {
2292 // Don't print anything if it is a NULL help token
2294 if (MenuOption
->ThisTag
->Help
== 0) {
2297 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2300 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2302 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2304 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2306 // Pad String with spaces to simulate a clearing of the previous line
2308 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2309 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2313 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2315 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2320 // Reset this flag every time we finish using it.
2326 case CfPrepareToReadKey
:
2327 ControlFlag
= CfReadKey
;
2328 ScreenOperation
= UiNoOperation
;
2332 ControlFlag
= CfScreenOperation
;
2335 // Wait for user's selection
2338 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2339 } while (Status
== EFI_TIMEOUT
);
2341 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2343 // IFR is updated in Callback of refresh opcode, re-parse it
2345 Selection
->Statement
= NULL
;
2349 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2351 // If we encounter error, continue to read another key in.
2353 if (EFI_ERROR (Status
)) {
2354 ControlFlag
= CfReadKey
;
2358 switch (Key
.UnicodeChar
) {
2359 case CHAR_CARRIAGE_RETURN
:
2360 ScreenOperation
= UiSelect
;
2365 // We will push the adjustment of these numeric values directly to the input handler
2366 // NOTE: we won't handle manual input numeric
2370 Statement
= MenuOption
->ThisTag
;
2371 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2372 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2373 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2375 if (Key
.UnicodeChar
== '+') {
2376 gDirection
= SCAN_RIGHT
;
2378 gDirection
= SCAN_LEFT
;
2380 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2381 if (EFI_ERROR (Status
)) {
2383 // Repaint to clear possible error prompt pop-up
2388 if (OptionString
!= NULL
) {
2389 FreePool (OptionString
);
2395 ScreenOperation
= UiUp
;
2400 ScreenOperation
= UiDown
;
2404 if (gClassOfVfr
!= FORMSET_CLASS_FRONT_PAGE
) {
2405 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2406 ScreenOperation
= UiSelect
;
2412 if (((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2413 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2416 // If the function key has been disabled, just ignore the key.
2419 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2420 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2421 if (Key
.ScanCode
== SCAN_F9
) {
2423 // Reset to standard default
2425 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2427 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2436 case CfScreenOperation
:
2437 if (ScreenOperation
!= UiReset
) {
2439 // If the screen has no menu items, and the user didn't select UiReset
2440 // ignore the selection and go back to reading keys.
2442 if (IsListEmpty (&Menu
)) {
2443 ControlFlag
= CfReadKey
;
2447 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2449 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2450 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2451 if (IsSelectable (NextMenuOption
)) {
2456 if (Link
== &Menu
) {
2457 ControlFlag
= CfPrepareToReadKey
;
2463 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2466 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2467 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2474 ControlFlag
= CfCheckSelection
;
2476 Statement
= MenuOption
->ThisTag
;
2477 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2478 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2479 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2480 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2485 // Keep highlight on current MenuOption
2487 Selection
->QuestionId
= Statement
->QuestionId
;
2489 switch (Statement
->Operand
) {
2490 case EFI_IFR_REF_OP
:
2491 if (Statement
->RefDevicePath
!= 0) {
2493 // Goto another Hii Package list
2495 ControlFlag
= CfCheckSelection
;
2496 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2498 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2499 if (StringPtr
== NULL
) {
2501 // No device path string not found, exit
2503 Selection
->Action
= UI_ACTION_EXIT
;
2504 Selection
->Statement
= NULL
;
2507 BufferSize
= StrLen (StringPtr
) / 2;
2508 DevicePath
= AllocatePool (BufferSize
);
2511 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2513 DevicePathBuffer
= (UINT8
*) DevicePath
;
2514 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
2515 TemStr
[0] = StringPtr
[Index
];
2516 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
2517 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
2519 // Invalid Hex Char as the tail.
2523 if ((Index
& 1) == 0) {
2524 DevicePathBuffer
[Index
/2] = DigitUint8
;
2526 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2530 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2531 if (Selection
->Handle
== NULL
) {
2533 // If target Hii Handle not found, exit
2535 Selection
->Action
= UI_ACTION_EXIT
;
2536 Selection
->Statement
= NULL
;
2540 FreePool (StringPtr
);
2541 FreePool (DevicePath
);
2543 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2544 Selection
->FormId
= Statement
->RefFormId
;
2545 Selection
->QuestionId
= Statement
->RefQuestionId
;
2546 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2548 // Goto another Formset, check for uncommitted data
2550 ControlFlag
= CfCheckSelection
;
2551 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2553 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2554 Selection
->FormId
= Statement
->RefFormId
;
2555 Selection
->QuestionId
= Statement
->RefQuestionId
;
2556 } else if (Statement
->RefFormId
!= 0) {
2558 // Goto another form inside this formset,
2560 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2563 // Link current form so that we can always go back when someone hits the ESC
2565 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Statement
->RefFormId
);
2566 if (MenuList
== NULL
) {
2567 MenuList
= UiAddMenuList (CurrentMenu
, &Selection
->FormSetGuid
, Statement
->RefFormId
);
2570 Selection
->FormId
= Statement
->RefFormId
;
2571 Selection
->QuestionId
= Statement
->RefQuestionId
;
2572 } else if (Statement
->RefQuestionId
!= 0) {
2574 // Goto another Question
2576 Selection
->QuestionId
= Statement
->RefQuestionId
;
2578 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2579 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2588 case EFI_IFR_ACTION_OP
:
2590 // Process the Config string <ConfigResp>
2592 Status
= ProcessQuestionConfig (Selection
, Statement
);
2594 if (EFI_ERROR (Status
)) {
2599 // The action button may change some Question value, so refresh the form
2601 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2604 case EFI_IFR_RESET_BUTTON_OP
:
2606 // Reset Question to default value specified by DefaultId
2608 ControlFlag
= CfUiDefault
;
2609 DefaultId
= Statement
->DefaultId
;
2614 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2616 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
2617 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2619 if (EFI_ERROR (Status
)) {
2622 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2624 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2627 if (OptionString
!= NULL
) {
2628 FreePool (OptionString
);
2636 // We come here when someone press ESC
2638 ControlFlag
= CfCheckSelection
;
2640 if (CurrentMenu
->Parent
!= NULL
) {
2642 // we have a parent, so go to the parent menu
2644 if (CompareGuid (&CurrentMenu
->FormSetGuid
, &CurrentMenu
->Parent
->FormSetGuid
)) {
2646 // The parent menu and current menu are in the same formset
2648 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2650 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2652 Selection
->Statement
= NULL
;
2654 Selection
->FormId
= CurrentMenu
->Parent
->FormId
;
2655 Selection
->QuestionId
= CurrentMenu
->Parent
->QuestionId
;
2658 // Clear highlight record for this menu
2660 CurrentMenu
->QuestionId
= 0;
2664 if (gClassOfVfr
== FORMSET_CLASS_FRONT_PAGE
) {
2666 // We never exit FrontPage, so skip the ESC
2668 Selection
->Action
= UI_ACTION_NONE
;
2673 // We are going to leave current FormSet, so check uncommited data in this FormSet
2675 if (gNvUpdateRequired
) {
2676 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2678 YesResponse
= gYesResponse
[0];
2679 NoResponse
= gNoResponse
[0];
2682 // If NV flag is up, prompt user
2685 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveChanges
, gAreYouSure
, gEmptyString
);
2688 (Key
.ScanCode
!= SCAN_ESC
) &&
2689 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2690 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2693 if (Key
.ScanCode
== SCAN_ESC
) {
2695 // User hits the ESC key
2700 Selection
->Action
= UI_ACTION_NONE
;
2705 // If the user hits the YesResponse key
2707 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2708 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2712 Selection
->Action
= UI_ACTION_EXIT
;
2713 Selection
->Statement
= NULL
;
2714 CurrentMenu
->QuestionId
= 0;
2719 ControlFlag
= CfCheckSelection
;
2720 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2721 if (MenuOption
->Sequence
!= 0) {
2723 // In the middle or tail of the Date/Time op-code set, go left.
2725 NewPos
= NewPos
->BackLink
;
2731 ControlFlag
= CfCheckSelection
;
2732 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2733 if (MenuOption
->Sequence
!= 2) {
2735 // In the middle or tail of the Date/Time op-code set, go left.
2737 NewPos
= NewPos
->ForwardLink
;
2743 ControlFlag
= CfCheckSelection
;
2745 SavedListEntry
= TopOfScreen
;
2747 if (NewPos
->BackLink
!= &Menu
) {
2750 // Adjust Date/Time position before we advance forward.
2752 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2755 // Caution that we have already rewind to the top, don't go backward in this situation.
2757 if (NewPos
->BackLink
!= &Menu
) {
2758 NewPos
= NewPos
->BackLink
;
2761 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2762 DistanceValue
= PreviousMenuOption
->Skip
;
2765 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2766 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2767 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2768 // checking can be done.
2770 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2773 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2774 // don't worry about a redraw.
2776 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2778 TopOfScreen
= NewPos
;
2781 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2782 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2783 DistanceValue
+= PreviousMenuOption
->Skip
;
2785 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2786 if (Difference
> 0) {
2788 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2790 TopOfScreen
= NewPos
;
2796 if (Difference
< 0) {
2798 // We want to goto previous MenuOption, but finally we go down.
2799 // it means that we hit the begining MenuOption that can be focused
2800 // so we simply scroll to the top
2802 if (SavedListEntry
!= Menu
.ForwardLink
) {
2803 TopOfScreen
= Menu
.ForwardLink
;
2809 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2811 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2813 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2815 SavedMenuOption
= MenuOption
;
2816 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2817 if (!IsSelectable (MenuOption
)) {
2819 // If we are at the end of the list and sitting on a text op, we need to more forward
2821 ScreenOperation
= UiDown
;
2822 ControlFlag
= CfScreenOperation
;
2826 MenuOption
= SavedMenuOption
;
2831 ControlFlag
= CfCheckSelection
;
2833 if (NewPos
->BackLink
== &Menu
) {
2842 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2844 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2845 Index
= Index
- PreviousMenuOption
->Skip
;
2846 Link
= Link
->BackLink
;
2847 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2851 Difference
= MoveToNextStatement (TRUE
, &Link
);
2852 if (Difference
> 0) {
2854 // The focus MenuOption is above the TopOfScreen
2857 } else if (Difference
< 0) {
2859 // This happens when there is no MenuOption can be focused from
2860 // Current MenuOption to the first MenuOption
2862 TopOfScreen
= Menu
.ForwardLink
;
2864 Index
+= Difference
;
2865 if (Index
< TopRow
) {
2869 if (NewPos
== Link
) {
2877 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2878 // Don't do this when we are already in the first page.
2880 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2881 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2885 ControlFlag
= CfCheckSelection
;
2887 if (NewPos
->ForwardLink
== &Menu
) {
2896 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2898 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2899 Index
= Index
+ NextMenuOption
->Skip
;
2900 Link
= Link
->ForwardLink
;
2901 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2904 Index
+= MoveToNextStatement (FALSE
, &Link
);
2905 if (Index
> BottomRow
) {
2907 // There are more MenuOption needing scrolling
2912 if (NewPos
== Link
&& Index
<= BottomRow
) {
2914 // Finally we know that NewPos is the last MenuOption can be focused.
2923 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2924 // Don't do this when we are already in the last page.
2926 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2927 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2931 ControlFlag
= CfCheckSelection
;
2933 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2934 // to be one that progresses to the next set of op-codes, we need to advance to the last
2935 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2936 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2937 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2938 // the Date/Time op-code.
2940 SavedListEntry
= NewPos
;
2941 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2943 if (NewPos
->ForwardLink
!= &Menu
) {
2944 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2946 NewPos
= NewPos
->ForwardLink
;
2947 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2949 DistanceValue
+= NextMenuOption
->Skip
;
2950 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2952 // An option might be multi-line, so we need to reflect that data in the overall skip value
2954 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2956 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2957 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2958 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2959 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2965 // If we are going to scroll, update TopOfScreen
2967 if (Temp
> BottomRow
) {
2970 // Is the current top of screen a zero-advance op-code?
2971 // If so, keep moving forward till we hit a >0 advance op-code
2973 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2976 // If bottom op-code is more than one line or top op-code is more than one line
2978 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2980 // Is the bottom op-code greater than or equal in size to the top op-code?
2982 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2984 // Skip the top op-code
2986 TopOfScreen
= TopOfScreen
->ForwardLink
;
2987 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2989 OldSkipValue
= Difference
;
2991 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2994 // If we have a remainder, skip that many more op-codes until we drain the remainder
2997 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2998 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
3001 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3003 TopOfScreen
= TopOfScreen
->ForwardLink
;
3004 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3005 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
3006 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
3009 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
3010 TopOfScreen
= TopOfScreen
->ForwardLink
;
3011 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3012 Difference
= SavedMenuOption
->Skip
- Difference
;
3018 // Since we will act on this op-code in the next routine, and increment the
3019 // SkipValue, set the skips to one less than what is required.
3021 SkipValue
= Difference
- 1;
3025 // Since we will act on this op-code in the next routine, and increment the
3026 // SkipValue, set the skips to one less than what is required.
3028 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3031 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3032 TopOfScreen
= TopOfScreen
->ForwardLink
;
3035 SkipValue
= OldSkipValue
;
3039 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3040 // Let's set a skip flag to smoothly scroll the top of the screen.
3042 if (SavedMenuOption
->Skip
> 1) {
3043 if (SavedMenuOption
== NextMenuOption
) {
3050 TopOfScreen
= TopOfScreen
->ForwardLink
;
3052 } while (SavedMenuOption
->Skip
== 0);
3055 OldSkipValue
= SkipValue
;
3058 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3060 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3063 SavedMenuOption
= MenuOption
;
3064 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3065 if (!IsSelectable (MenuOption
)) {
3067 // If we are at the end of the list and sitting on a text op, we need to more forward
3069 ScreenOperation
= UiUp
;
3070 ControlFlag
= CfScreenOperation
;
3074 MenuOption
= SavedMenuOption
;
3076 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3078 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3083 ControlFlag
= CfCheckSelection
;
3088 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
3090 if (!EFI_ERROR (Status
)) {
3091 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3092 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3095 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3096 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3104 ControlFlag
= CfCheckSelection
;
3105 if (!Selection
->FormEditable
) {
3107 // This Form is not editable, ignore the F9 (reset to default)
3112 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
3114 if (!EFI_ERROR (Status
)) {
3115 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3116 Selection
->Statement
= NULL
;
3119 // Show NV update flag on status bar
3121 gNvUpdateRequired
= TRUE
;
3125 case CfUiNoOperation
:
3126 ControlFlag
= CfCheckSelection
;
3130 UiFreeRefreshList ();
3132 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3133 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3134 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3135 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");