2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2011, 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_BROWSER_ACTION_REQUEST ActionRequest
;
346 if (gMenuRefreshHead
!= NULL
) {
348 MenuRefreshEntry
= gMenuRefreshHead
;
351 // Reset FormPackage update flag
353 mHiiPackageListUpdated
= FALSE
;
356 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
358 Selection
= MenuRefreshEntry
->Selection
;
359 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
361 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
362 if (EFI_ERROR (Status
)) {
367 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
369 if (OptionString
!= NULL
) {
371 // If leading spaces on OptionString - remove the spaces
373 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
376 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, &OptionString
[Index
]);
377 FreePool (OptionString
);
381 // Question value may be changed, need invoke its Callback()
383 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
384 if (((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) && (ConfigAccess
!= NULL
)) {
385 ActionRequest
= EFI_BROWSER_ACTION_REQUEST_NONE
;
386 Status
= ConfigAccess
->Callback (
388 EFI_BROWSER_ACTION_CHANGING
,
389 Question
->QuestionId
,
390 Question
->HiiValue
.Type
,
391 &Question
->HiiValue
.Value
,
394 if (!EFI_ERROR (Status
)) {
395 switch (ActionRequest
) {
396 case EFI_BROWSER_ACTION_REQUEST_RESET
:
397 gResetRequired
= TRUE
;
400 case EFI_BROWSER_ACTION_REQUEST_SUBMIT
:
401 SubmitForm (Selection
->FormSet
, Selection
->Form
);
404 case EFI_BROWSER_ACTION_REQUEST_EXIT
:
405 Selection
->Action
= UI_ACTION_EXIT
;
406 gNvUpdateRequired
= FALSE
;
415 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
417 } while (MenuRefreshEntry
!= NULL
);
419 if (mHiiPackageListUpdated
) {
421 // Package list is updated, force to reparse IFR binary of target Formset
423 mHiiPackageListUpdated
= FALSE
;
424 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
434 Wait for a given event to fire, or for an optional timeout to expire.
436 @param Event The event to wait for
437 @param Timeout An optional timeout value in 100 ns units.
438 @param RefreshInterval Menu refresh interval (in seconds).
440 @retval EFI_SUCCESS Event fired before Timeout expired.
441 @retval EFI_TIME_OUT Timout expired before Event fired.
445 UiWaitForSingleEvent (
447 IN UINT64 Timeout
, OPTIONAL
448 IN UINT8 RefreshInterval OPTIONAL
453 EFI_EVENT TimerEvent
;
454 EFI_EVENT WaitList
[2];
458 // Create a timer event
460 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
461 if (!EFI_ERROR (Status
)) {
463 // Set the timer event
472 // Wait for the original event or the timer
475 WaitList
[1] = TimerEvent
;
476 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
477 gBS
->CloseEvent (TimerEvent
);
480 // If the timer expired, change the return to timed out
482 if (!EFI_ERROR (Status
) && Index
== 1) {
483 Status
= EFI_TIMEOUT
;
488 // Update screen every second
490 if (RefreshInterval
== 0) {
491 Timeout
= ONE_SECOND
;
493 Timeout
= RefreshInterval
* ONE_SECOND
;
497 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
500 // Set the timer event
509 // Wait for the original event or the timer
512 WaitList
[1] = TimerEvent
;
513 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
516 // If the timer expired, update anything that needs a refresh and keep waiting
518 if (!EFI_ERROR (Status
) && Index
== 1) {
519 Status
= EFI_TIMEOUT
;
520 if (RefreshInterval
!= 0) {
521 Status
= RefreshForm ();
525 gBS
->CloseEvent (TimerEvent
);
526 } while (Status
== EFI_TIMEOUT
);
534 Add one menu option by specified description and context.
536 @param String String description for this option.
537 @param Handle Hii handle for the package list.
538 @param Statement Statement of this Menu Option.
539 @param NumberOfLines Display lines for this Menu Option.
540 @param MenuItemCount The index for this Option in the Menu.
542 @retval Pointer Pointer to the added Menu Option.
548 IN EFI_HII_HANDLE Handle
,
549 IN FORM_BROWSER_STATEMENT
*Statement
,
550 IN UINT16 NumberOfLines
,
551 IN UINT16 MenuItemCount
554 UI_MENU_OPTION
*MenuOption
;
561 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
563 // Add three MenuOptions for Date/Time
564 // Data format : [01/02/2004] [11:22:33]
565 // Line number : 0 0 1 0 0 1
570 if (Statement
->Storage
== NULL
) {
572 // For RTC type of date/time, set default refresh interval to be 1 second
574 if (Statement
->RefreshInterval
== 0) {
575 Statement
->RefreshInterval
= 1;
580 for (Index
= 0; Index
< Count
; Index
++) {
581 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
584 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
585 MenuOption
->Description
= String
;
586 MenuOption
->Handle
= Handle
;
587 MenuOption
->ThisTag
= Statement
;
588 MenuOption
->EntryNumber
= MenuItemCount
;
592 // Override LineNumber for the MenuOption in Date/Time sequence
594 MenuOption
->Skip
= 1;
596 MenuOption
->Skip
= NumberOfLines
;
598 MenuOption
->Sequence
= Index
;
600 if (Statement
->GrayOutExpression
!= NULL
) {
601 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
604 switch (Statement
->Operand
) {
605 case EFI_IFR_ORDERED_LIST_OP
:
606 case EFI_IFR_ONE_OF_OP
:
607 case EFI_IFR_NUMERIC_OP
:
608 case EFI_IFR_TIME_OP
:
609 case EFI_IFR_DATE_OP
:
610 case EFI_IFR_CHECKBOX_OP
:
611 case EFI_IFR_PASSWORD_OP
:
612 case EFI_IFR_STRING_OP
:
614 // User could change the value of these items
616 MenuOption
->IsQuestion
= TRUE
;
620 MenuOption
->IsQuestion
= FALSE
;
624 if ((Statement
->ValueExpression
!= NULL
) ||
625 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
626 MenuOption
->ReadOnly
= TRUE
;
629 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
637 Routine used to abstract a generic dialog interface and return the selected key or string
639 @param NumberOfLines The number of lines for the dialog box
640 @param HotKey Defines whether a single character is parsed
641 (TRUE) and returned in KeyValue or a string is
642 returned in StringBuffer. Two special characters
643 are considered when entering a string, a SCAN_ESC
644 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
645 string input and returns
646 @param MaximumStringSize The maximum size in bytes of a typed in string
647 (each character is a CHAR16) and the minimum
648 string returned is two bytes
649 @param StringBuffer The passed in pointer to the buffer which will
650 hold the typed in string if HotKey is FALSE
651 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
652 @param ... A series of (quantity == NumberOfLines) text
653 strings which will be used to construct the dialog
656 @retval EFI_SUCCESS Displayed dialog and received user interaction
657 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
658 (StringBuffer == NULL) && (HotKey == FALSE))
659 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
665 IN UINTN NumberOfLines
,
667 IN UINTN MaximumStringSize
,
668 OUT CHAR16
*StringBuffer
,
669 OUT EFI_INPUT_KEY
*KeyValue
,
678 CHAR16
*BufferedString
;
685 BOOLEAN SelectionComplete
;
687 UINTN CurrentAttribute
;
688 UINTN DimensionsWidth
;
689 UINTN DimensionsHeight
;
691 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
692 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
694 SelectionComplete
= FALSE
;
696 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
697 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
698 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
701 ASSERT (BufferedString
);
703 VA_START (Marker
, KeyValue
);
706 // Zero the outgoing buffer
708 ZeroMem (StringBuffer
, MaximumStringSize
);
711 if (KeyValue
== NULL
) {
712 return EFI_INVALID_PARAMETER
;
715 if (StringBuffer
== NULL
) {
716 return EFI_INVALID_PARAMETER
;
722 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
727 // Determine the largest string in the dialog box
728 // Notice we are starting with 1 since String is the first string
730 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
731 StackString
= VA_ARG (Marker
, CHAR16
*);
733 if (StackString
[0] == L
' ') {
734 InputOffset
= Count
+ 1;
737 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
739 // Size of the string visually and subtract the width by one for the null-terminator
741 LargestString
= (GetStringWidth (StackString
) / 2);
746 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
747 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
754 VA_START (Marker
, KeyValue
);
755 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
759 // Take the first key typed and report it back?
762 Status
= WaitForKeyStroke (&Key
);
763 ASSERT_EFI_ERROR (Status
);
764 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
768 Status
= WaitForKeyStroke (&Key
);
770 switch (Key
.UnicodeChar
) {
772 switch (Key
.ScanCode
) {
774 FreePool (TempString
);
775 FreePool (BufferedString
);
776 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
777 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
778 return EFI_DEVICE_ERROR
;
786 case CHAR_CARRIAGE_RETURN
:
787 SelectionComplete
= TRUE
;
788 FreePool (TempString
);
789 FreePool (BufferedString
);
790 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
791 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
796 if (StringBuffer
[0] != CHAR_NULL
) {
797 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
798 TempString
[Index
] = StringBuffer
[Index
];
801 // Effectively truncate string by 1 character
803 TempString
[Index
- 1] = CHAR_NULL
;
804 StrCpy (StringBuffer
, TempString
);
809 // If it is the beginning of the string, don't worry about checking maximum limits
811 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
812 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
813 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
814 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
815 KeyPad
[0] = Key
.UnicodeChar
;
816 KeyPad
[1] = CHAR_NULL
;
817 StrCat (StringBuffer
, KeyPad
);
818 StrCat (TempString
, KeyPad
);
821 // If the width of the input string is now larger than the screen, we nee to
822 // adjust the index to start printing portions of the string
824 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
826 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
828 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
829 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
834 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
835 BufferedString
[Count
] = StringBuffer
[Index
];
838 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
841 } while (!SelectionComplete
);
844 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
845 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
850 Draw a pop up windows based on the dimension, number of lines and
853 @param RequestedWidth The width of the pop-up.
854 @param NumberOfLines The number of lines.
855 @param Marker The variable argument list for the list of string to be printed.
860 IN UINTN RequestedWidth
,
861 IN UINTN NumberOfLines
,
873 UINTN DimensionsWidth
;
874 UINTN DimensionsHeight
;
876 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
877 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
879 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
881 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
882 RequestedWidth
= DimensionsWidth
- 2;
886 // Subtract the PopUp width from total Columns, allow for one space extra on
887 // each end plus a border.
889 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
890 End
= Start
+ RequestedWidth
+ 1;
892 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
893 Bottom
= Top
+ NumberOfLines
+ 2;
895 Character
= BOXDRAW_DOWN_RIGHT
;
896 PrintCharAt (Start
, Top
, Character
);
897 Character
= BOXDRAW_HORIZONTAL
;
898 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
899 PrintChar (Character
);
902 Character
= BOXDRAW_DOWN_LEFT
;
903 PrintChar (Character
);
904 Character
= BOXDRAW_VERTICAL
;
907 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
908 String
= VA_ARG (Marker
, CHAR16
*);
911 // This will clear the background of the line - we never know who might have been
912 // here before us. This differs from the next clear in that it used the non-reverse
913 // video for normal printing.
915 if (GetStringWidth (String
) / 2 > 1) {
916 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
920 // Passing in a space results in the assumption that this is where typing will occur
922 if (String
[0] == L
' ') {
923 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
927 // Passing in a NULL results in a blank space
929 if (String
[0] == CHAR_NULL
) {
930 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
934 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
938 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
939 PrintCharAt (Start
, Index
+ 1, Character
);
940 PrintCharAt (End
- 1, Index
+ 1, Character
);
943 Character
= BOXDRAW_UP_RIGHT
;
944 PrintCharAt (Start
, Bottom
- 1, Character
);
945 Character
= BOXDRAW_HORIZONTAL
;
946 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
947 PrintChar (Character
);
950 Character
= BOXDRAW_UP_LEFT
;
951 PrintChar (Character
);
955 Draw a pop up windows based on the dimension, number of lines and
958 @param RequestedWidth The width of the pop-up.
959 @param NumberOfLines The number of lines.
960 @param ... A series of text strings that displayed in the pop-up.
965 CreateMultiStringPopUp (
966 IN UINTN RequestedWidth
,
967 IN UINTN NumberOfLines
,
973 VA_START (Marker
, NumberOfLines
);
975 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
982 Update status bar on the bottom of menu.
984 @param MessageType The type of message to be shown.
985 @param Flags The flags in Question header.
986 @param State Set or clear.
991 IN UINTN MessageType
,
997 CHAR16
*NvUpdateMessage
;
998 CHAR16
*InputErrorMessage
;
1000 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1001 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1003 switch (MessageType
) {
1006 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1008 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1009 gScreenDimensions
.BottomRow
- 1,
1014 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1015 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1016 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1019 mInputError
= FALSE
;
1023 case NV_UPDATE_REQUIRED
:
1024 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
1026 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1028 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1029 gScreenDimensions
.BottomRow
- 1,
1032 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1034 gNvUpdateRequired
= TRUE
;
1036 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1037 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1039 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1040 gScreenDimensions
.BottomRow
- 1,
1045 gNvUpdateRequired
= FALSE
;
1050 case REFRESH_STATUS_BAR
:
1052 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
1055 if (gNvUpdateRequired
) {
1056 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1064 FreePool (InputErrorMessage
);
1065 FreePool (NvUpdateMessage
);
1071 Get the supported width for a particular op-code
1073 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1074 @param Handle The handle in the HII database being used
1076 @return Returns the number of CHAR16 characters that is support.
1081 IN FORM_BROWSER_STATEMENT
*Statement
,
1082 IN EFI_HII_HANDLE Handle
1092 // See if the second text parameter is really NULL
1094 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1095 String
= GetToken (Statement
->TextTwo
, Handle
);
1096 Size
= StrLen (String
);
1100 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1101 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1102 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1103 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1104 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1106 // Allow a wide display if text op-code and no secondary text op-code
1108 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1110 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1112 Width
= (UINT16
) gPromptBlockWidth
;
1115 if (Statement
->InSubtitle
) {
1116 Width
-= SUBTITLE_INDENT
;
1119 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1123 Will copy LineWidth amount of a string in the OutputString buffer and return the
1124 number of CHAR16 characters that were copied into the OutputString buffer.
1126 @param InputString String description for this option.
1127 @param LineWidth Width of the desired string to extract in CHAR16
1129 @param Index Where in InputString to start the copy process
1130 @param OutputString Buffer to copy the string into
1132 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1137 IN CHAR16
*InputString
,
1138 IN UINT16 LineWidth
,
1139 IN OUT UINTN
*Index
,
1140 OUT CHAR16
**OutputString
1146 if (GetLineByWidthFinished
) {
1147 GetLineByWidthFinished
= FALSE
;
1154 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1157 // Ensure we have got a valid buffer
1159 if (*OutputString
!= NULL
) {
1162 //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.
1163 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1165 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1166 *Index
= *Index
+ 2;
1170 // Fast-forward the string and see if there is a carriage-return in the string
1172 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1176 // Copy the desired LineWidth of data to the output buffer.
1177 // Also make sure that we don't copy more than the string.
1178 // Also make sure that if there are linefeeds, we account for them.
1180 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1181 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1184 // Convert to CHAR16 value and show that we are done with this operation
1186 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1187 if (LineWidth
!= 0) {
1188 GetLineByWidthFinished
= TRUE
;
1191 if (Count2
== LineWidth
) {
1193 // Rewind the string from the maximum size until we see a space to break the line
1195 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1197 if (LineWidth
== 0) {
1205 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1208 // If currently pointing to a space, increment the index to the first non-space character
1211 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1215 *Index
= (UINT16
) (*Index
+ LineWidth
);
1224 Update display lines for a Menu Option.
1226 @param Selection The user's selection.
1227 @param MenuOption The MenuOption to be checked.
1228 @param OptionalString The option string.
1229 @param SkipValue The number of lins to skip.
1233 UpdateOptionSkipLines (
1234 IN UI_MENU_SELECTION
*Selection
,
1235 IN UI_MENU_OPTION
*MenuOption
,
1236 OUT CHAR16
**OptionalString
,
1244 CHAR16
*OutputString
;
1245 CHAR16
*OptionString
;
1248 OptionString
= *OptionalString
;
1249 OutputString
= NULL
;
1251 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1253 if (OptionString
!= NULL
) {
1254 Width
= (UINT16
) gOptionBlockWidth
;
1258 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1260 // If there is more string to process print on the next row and increment the Skip value
1262 if (StrLen (&OptionString
[Index
]) != 0) {
1263 if (SkipValue
== 0) {
1266 // Since the Number of lines for this menu entry may or may not be reflected accurately
1267 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1268 // some testing to ensure we are keeping this in-sync.
1270 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1272 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1278 FreePool (OutputString
);
1279 if (SkipValue
!= 0) {
1287 *OptionalString
= OptionString
;
1292 Check whether this Menu Option could be highlighted.
1294 This is an internal function.
1296 @param MenuOption The MenuOption to be checked.
1298 @retval TRUE This Menu Option is selectable.
1299 @retval FALSE This Menu Option could not be selected.
1304 UI_MENU_OPTION
*MenuOption
1307 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1308 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1317 Determine if the menu is the last menu that can be selected.
1319 This is an internal function.
1321 @param Direction The scroll direction. False is down. True is up.
1322 @param CurrentPos The current focus.
1324 @return FALSE -- the menu isn't the last menu that can be selected.
1325 @return TRUE -- the menu is the last menu that can be selected.
1330 IN BOOLEAN Direction
,
1331 IN LIST_ENTRY
*CurrentPos
1335 UI_MENU_OPTION
*MenuOption
;
1337 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1339 if (Temp
== &gMenuOption
) {
1343 for (; Temp
!= &gMenuOption
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1344 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1345 if (IsSelectable (MenuOption
)) {
1355 Move to next selectable statement.
1357 This is an internal function.
1359 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1360 @param CurrentPosition Current position.
1362 @return The row distance from current MenuOption to next selectable MenuOption.
1366 MoveToNextStatement (
1368 IN OUT LIST_ENTRY
**CurrentPosition
1374 UI_MENU_OPTION
*NextMenuOption
;
1377 Pos
= *CurrentPosition
;
1381 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1382 if (IsSelectable (NextMenuOption
)) {
1385 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1389 Distance
+= NextMenuOption
->Skip
;
1390 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1395 // If we hit end there is still no statement can be focused,
1396 // we go backwards to find the statement can be focused.
1399 Pos
= *CurrentPosition
;
1402 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1403 if (IsSelectable (NextMenuOption
)) {
1406 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1410 Distance
-= NextMenuOption
->Skip
;
1411 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1415 *CurrentPosition
= &NextMenuOption
->Link
;
1421 Adjust Data and Time position accordingly.
1422 Data format : [01/02/2004] [11:22:33]
1423 Line number : 0 0 1 0 0 1
1425 This is an internal function.
1427 @param DirectionUp the up or down direction. False is down. True is
1429 @param CurrentPosition Current position. On return: Point to the last
1430 Option (Year or Second) if up; Point to the first
1431 Option (Month or Hour) if down.
1433 @return Return line number to pad. It is possible that we stand on a zero-advance
1434 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1438 AdjustDateAndTimePosition (
1439 IN BOOLEAN DirectionUp
,
1440 IN OUT LIST_ENTRY
**CurrentPosition
1444 LIST_ENTRY
*NewPosition
;
1445 UI_MENU_OPTION
*MenuOption
;
1446 UINTN PadLineNumber
;
1449 NewPosition
= *CurrentPosition
;
1450 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1452 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1453 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1455 // Calculate the distance from current position to the last Date/Time MenuOption
1458 while (MenuOption
->Skip
== 0) {
1460 NewPosition
= NewPosition
->ForwardLink
;
1461 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1465 NewPosition
= *CurrentPosition
;
1468 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1469 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1470 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1471 // checking can be done.
1473 while (Count
++ < 2) {
1474 NewPosition
= NewPosition
->BackLink
;
1478 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1479 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1480 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1481 // checking can be done.
1483 while (Count
-- > 0) {
1484 NewPosition
= NewPosition
->ForwardLink
;
1488 *CurrentPosition
= NewPosition
;
1491 return PadLineNumber
;
1495 Find HII Handle in the HII database associated with given Device Path.
1497 If DevicePath is NULL, then ASSERT.
1499 @param DevicePath Device Path associated with the HII package list
1502 @retval Handle HII package list Handle associated with the Device
1504 @retval NULL Hii Package list handle is not found.
1509 DevicePathToHiiHandle (
1510 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1514 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1519 EFI_HANDLE DriverHandle
;
1520 EFI_HII_HANDLE
*HiiHandles
;
1521 EFI_HII_HANDLE HiiHandle
;
1523 ASSERT (DevicePath
!= NULL
);
1525 TmpDevicePath
= DevicePath
;
1527 // Locate Device Path Protocol handle buffer
1529 Status
= gBS
->LocateDevicePath (
1530 &gEfiDevicePathProtocolGuid
,
1534 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1539 // Retrieve all HII Handles from HII database
1541 BufferSize
= 0x1000;
1542 HiiHandles
= AllocatePool (BufferSize
);
1543 ASSERT (HiiHandles
!= NULL
);
1544 Status
= mHiiDatabase
->ListPackageLists (
1546 EFI_HII_PACKAGE_TYPE_ALL
,
1551 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1552 FreePool (HiiHandles
);
1553 HiiHandles
= AllocatePool (BufferSize
);
1554 ASSERT (HiiHandles
!= NULL
);
1556 Status
= mHiiDatabase
->ListPackageLists (
1558 EFI_HII_PACKAGE_TYPE_ALL
,
1565 if (EFI_ERROR (Status
)) {
1566 FreePool (HiiHandles
);
1571 // Search Hii Handle by Driver Handle
1574 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1575 for (Index
= 0; Index
< HandleCount
; Index
++) {
1576 Status
= mHiiDatabase
->GetPackageListHandle (
1581 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1582 HiiHandle
= HiiHandles
[Index
];
1587 FreePool (HiiHandles
);
1592 Display menu and wait for user to select one menu option, then return it.
1593 If AutoBoot is enabled, then if user doesn't select any option,
1594 after period of time, it will automatically return the first menu option.
1596 @param Selection Menu selection.
1598 @retval EFI_SUCESSS This function always return successfully for now.
1603 IN OUT UI_MENU_SELECTION
*Selection
1609 UINTN DistanceValue
;
1621 CHAR16
*OptionString
;
1622 CHAR16
*OutputString
;
1623 CHAR16
*FormattedString
;
1635 LIST_ENTRY
*TopOfScreen
;
1636 LIST_ENTRY
*SavedListEntry
;
1637 UI_MENU_OPTION
*MenuOption
;
1638 UI_MENU_OPTION
*NextMenuOption
;
1639 UI_MENU_OPTION
*SavedMenuOption
;
1640 UI_MENU_OPTION
*PreviousMenuOption
;
1641 UI_CONTROL_FLAG ControlFlag
;
1642 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1643 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1644 UI_SCREEN_OPERATION ScreenOperation
;
1645 UINT8 MinRefreshInterval
;
1648 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1649 FORM_BROWSER_STATEMENT
*Statement
;
1651 UINT8
*DevicePathBuffer
;
1653 UI_MENU_LIST
*CurrentMenu
;
1654 UI_MENU_LIST
*MenuList
;
1655 FORM_BROWSER_FORM
*RefForm
;
1657 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1659 Status
= EFI_SUCCESS
;
1660 FormattedString
= NULL
;
1661 OptionString
= NULL
;
1662 ScreenOperation
= UiNoOperation
;
1664 MinRefreshInterval
= 0;
1667 OutputString
= NULL
;
1672 MenuRefreshEntry
= gMenuRefreshHead
;
1674 NextMenuOption
= NULL
;
1675 PreviousMenuOption
= NULL
;
1676 SavedMenuOption
= NULL
;
1679 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1681 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
1682 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1683 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1685 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1686 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1689 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
1690 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1692 Selection
->TopRow
= TopRow
;
1693 Selection
->BottomRow
= BottomRow
;
1694 Selection
->PromptCol
= Col
;
1695 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1696 Selection
->Statement
= NULL
;
1698 TopOfScreen
= gMenuOption
.ForwardLink
;
1703 // Find current Menu
1705 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
1706 if (CurrentMenu
== NULL
) {
1708 // Current menu not found, add it to the menu tree
1710 CurrentMenu
= UiAddMenuList (NULL
, &Selection
->FormSetGuid
, Selection
->FormId
);
1712 ASSERT (CurrentMenu
!= NULL
);
1714 if (Selection
->QuestionId
== 0) {
1716 // Highlight not specified, fetch it from cached menu
1718 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
1722 // Get user's selection
1724 NewPos
= gMenuOption
.ForwardLink
;
1726 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1727 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1729 ControlFlag
= CfInitialization
;
1730 Selection
->Action
= UI_ACTION_NONE
;
1732 switch (ControlFlag
) {
1733 case CfInitialization
:
1734 if (IsListEmpty (&gMenuOption
)) {
1735 ControlFlag
= CfReadKey
;
1737 ControlFlag
= CfCheckSelection
;
1741 case CfCheckSelection
:
1742 if (Selection
->Action
!= UI_ACTION_NONE
) {
1743 ControlFlag
= CfExit
;
1745 ControlFlag
= CfRepaint
;
1750 ControlFlag
= CfRefreshHighLight
;
1760 Temp
= (UINTN
) SkipValue
;
1761 Temp2
= (UINTN
) SkipValue
;
1764 LocalScreen
.LeftColumn
,
1765 LocalScreen
.RightColumn
,
1766 TopRow
- SCROLL_ARROW_HEIGHT
,
1767 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1768 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
1771 UiFreeRefreshList ();
1772 MinRefreshInterval
= 0;
1774 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
1775 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1776 MenuOption
->Row
= Row
;
1777 MenuOption
->Col
= Col
;
1778 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1780 Statement
= MenuOption
->ThisTag
;
1781 if (Statement
->InSubtitle
) {
1782 MenuOption
->Col
+= SUBTITLE_INDENT
;
1785 if (MenuOption
->GrayOut
) {
1786 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1788 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1789 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
1793 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1796 if (Statement
->Operand
== EFI_IFR_REF_OP
&&
1797 ((gClassOfVfr
& FORMSET_CLASS_PLATFORM_SETUP
) == FORMSET_CLASS_PLATFORM_SETUP
) &&
1798 MenuOption
->Col
> 2) {
1800 // Print Arrow for Goto button.
1803 MenuOption
->Col
- 2,
1806 GEOMETRICSHAPE_RIGHT_TRIANGLE
1810 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1811 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1812 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1815 // If there is more string to process print on the next row and increment the Skip value
1817 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
1823 FreePool (OutputString
);
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 if (MenuOption
->GrayOut
) {
1880 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
1882 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
1884 gMenuRefreshHead
= MenuRefreshEntry
;
1887 // Advance to the last entry
1889 for (MenuRefreshEntry
= gMenuRefreshHead
;
1890 MenuRefreshEntry
->Next
!= NULL
;
1891 MenuRefreshEntry
= MenuRefreshEntry
->Next
1894 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1895 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1896 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1897 MenuRefreshEntry
->MenuOption
= MenuOption
;
1898 MenuRefreshEntry
->Selection
= Selection
;
1899 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1900 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1901 if (MenuOption
->GrayOut
) {
1902 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
1904 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
1909 Width
= (UINT16
) gOptionBlockWidth
;
1912 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1913 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1914 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1917 // If there is more string to process print on the next row and increment the Skip value
1919 if (StrLen (&OptionString
[Index
]) != 0) {
1923 // Since the Number of lines for this menu entry may or may not be reflected accurately
1924 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1925 // some testing to ensure we are keeping this in-sync.
1927 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1929 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1935 FreePool (OutputString
);
1944 FreePool (OptionString
);
1947 // If this is a text op with secondary text information
1949 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1950 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1952 Width
= (UINT16
) gOptionBlockWidth
;
1955 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1956 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1957 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1960 // If there is more string to process print on the next row and increment the Skip value
1962 if (StrLen (&StringPtr
[Index
]) != 0) {
1966 // Since the Number of lines for this menu entry may or may not be reflected accurately
1967 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1968 // some testing to ensure we are keeping this in-sync.
1970 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1972 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1978 FreePool (OutputString
);
1985 FreePool (StringPtr
);
1987 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
1990 // Need to handle the bottom of the display
1992 if (MenuOption
->Skip
> 1) {
1993 Row
+= MenuOption
->Skip
- SkipValue
;
1996 Row
+= MenuOption
->Skip
;
1999 if (Row
> BottomRow
) {
2000 if (!ValueIsScroll (FALSE
, Link
)) {
2004 Row
= BottomRow
+ 1;
2009 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2014 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2016 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2017 TopRow
- SCROLL_ARROW_HEIGHT
,
2021 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2025 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2027 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2028 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2032 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2039 case CfRefreshHighLight
:
2041 // MenuOption: Last menu option that need to remove hilight
2042 // MenuOption is set to NULL in Repaint
2043 // NewPos: Current menu option that need to hilight
2045 ControlFlag
= CfUpdateHelpString
;
2048 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2049 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2051 SavedValue
= Repaint
;
2054 if (Selection
->QuestionId
!= 0) {
2055 NewPos
= gMenuOption
.ForwardLink
;
2056 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2058 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &gMenuOption
) {
2059 NewPos
= NewPos
->ForwardLink
;
2060 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2062 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2064 // Target Question found, find its MenuOption
2068 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2069 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2070 Index
+= SavedMenuOption
->Skip
;
2071 Link
= Link
->ForwardLink
;
2074 if (Link
!= NewPos
|| Index
> BottomRow
) {
2076 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2079 for (Index
= TopRow
; Index
<= BottomRow
; ) {
2080 Link
= Link
->BackLink
;
2081 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2082 Index
+= SavedMenuOption
->Skip
;
2084 TopOfScreen
= Link
->ForwardLink
;
2088 ControlFlag
= CfRepaint
;
2093 // Target Question not found, highlight the default menu option
2095 NewPos
= TopOfScreen
;
2098 Selection
->QuestionId
= 0;
2101 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2102 if (MenuOption
!= NULL
) {
2104 // Remove highlight on last Menu Option
2106 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2107 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2108 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2109 if (OptionString
!= NULL
) {
2110 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2111 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2114 // If leading spaces on OptionString - remove the spaces
2116 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2119 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2120 OptionString
[Count
] = OptionString
[Index
];
2124 OptionString
[Count
] = CHAR_NULL
;
2127 Width
= (UINT16
) gOptionBlockWidth
;
2128 OriginalRow
= MenuOption
->Row
;
2130 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2131 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2132 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2135 // If there is more string to process print on the next row and increment the Skip value
2137 if (StrLen (&OptionString
[Index
]) != 0) {
2141 FreePool (OutputString
);
2144 MenuOption
->Row
= OriginalRow
;
2146 FreePool (OptionString
);
2149 if (MenuOption
->GrayOut
) {
2150 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2151 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2152 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2155 OriginalRow
= MenuOption
->Row
;
2156 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2158 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2159 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2160 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2163 // If there is more string to process print on the next row and increment the Skip value
2165 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2169 FreePool (OutputString
);
2172 MenuOption
->Row
= OriginalRow
;
2173 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2179 // This is only possible if we entered this page and the first menu option is
2180 // a "non-menu" item. In that case, force it UiDown
2182 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2183 if (!IsSelectable (MenuOption
)) {
2184 ASSERT (ScreenOperation
== UiNoOperation
);
2185 ScreenOperation
= UiDown
;
2186 ControlFlag
= CfScreenOperation
;
2191 // This is the current selected statement
2193 Statement
= MenuOption
->ThisTag
;
2194 Selection
->Statement
= Statement
;
2196 // Record highlight for current menu
2198 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2201 // Set reverse attribute
2203 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2204 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2207 // Assuming that we have a refresh linked-list created, lets annotate the
2208 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2209 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2211 if (gMenuRefreshHead
!= NULL
) {
2212 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2213 if (MenuOption
->GrayOut
) {
2214 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2216 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2218 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2219 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2224 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2225 if (OptionString
!= NULL
) {
2226 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2228 // If leading spaces on OptionString - remove the spaces
2230 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2233 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2234 OptionString
[Count
] = OptionString
[Index
];
2238 OptionString
[Count
] = CHAR_NULL
;
2240 Width
= (UINT16
) gOptionBlockWidth
;
2242 OriginalRow
= MenuOption
->Row
;
2244 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2245 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2246 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2249 // If there is more string to process print on the next row and increment the Skip value
2251 if (StrLen (&OptionString
[Index
]) != 0) {
2255 FreePool (OutputString
);
2258 MenuOption
->Row
= OriginalRow
;
2260 FreePool (OptionString
);
2263 OriginalRow
= MenuOption
->Row
;
2265 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2267 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2268 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2269 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2272 // If there is more string to process print on the next row and increment the Skip value
2274 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2278 FreePool (OutputString
);
2281 MenuOption
->Row
= OriginalRow
;
2286 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2289 // Clear reverse attribute
2291 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2294 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2295 // if we didn't break halfway when process CfRefreshHighLight.
2297 Repaint
= SavedValue
;
2300 case CfUpdateHelpString
:
2301 ControlFlag
= CfPrepareToReadKey
;
2303 if (Repaint
|| NewLine
) {
2305 // Don't print anything if it is a NULL help token
2307 ASSERT(MenuOption
!= NULL
);
2308 if (MenuOption
->ThisTag
->Help
== 0) {
2311 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2314 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2316 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2318 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2320 // Pad String with spaces to simulate a clearing of the previous line
2322 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2323 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2327 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2329 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2334 // Reset this flag every time we finish using it.
2340 case CfPrepareToReadKey
:
2341 ControlFlag
= CfReadKey
;
2342 ScreenOperation
= UiNoOperation
;
2346 ControlFlag
= CfScreenOperation
;
2349 // Wait for user's selection
2352 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2353 } while (Status
== EFI_TIMEOUT
);
2355 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2357 // IFR is updated in Callback of refresh opcode, re-parse it
2359 Selection
->Statement
= NULL
;
2363 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2365 // If we encounter error, continue to read another key in.
2367 if (EFI_ERROR (Status
)) {
2368 ControlFlag
= CfReadKey
;
2372 switch (Key
.UnicodeChar
) {
2373 case CHAR_CARRIAGE_RETURN
:
2374 ScreenOperation
= UiSelect
;
2379 // We will push the adjustment of these numeric values directly to the input handler
2380 // NOTE: we won't handle manual input numeric
2385 // If the screen has no menu items, and the user didn't select UiReset
2386 // ignore the selection and go back to reading keys.
2388 if(IsListEmpty (&gMenuOption
)) {
2389 ControlFlag
= CfReadKey
;
2393 ASSERT(MenuOption
!= NULL
);
2394 Statement
= MenuOption
->ThisTag
;
2395 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2396 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2397 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2399 if (Key
.UnicodeChar
== '+') {
2400 gDirection
= SCAN_RIGHT
;
2402 gDirection
= SCAN_LEFT
;
2404 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2405 if (EFI_ERROR (Status
)) {
2407 // Repaint to clear possible error prompt pop-up
2412 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2414 if (OptionString
!= NULL
) {
2415 FreePool (OptionString
);
2421 ScreenOperation
= UiUp
;
2426 ScreenOperation
= UiDown
;
2430 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2432 // If the screen has no menu items, and the user didn't select UiReset
2433 // ignore the selection and go back to reading keys.
2435 if(IsListEmpty (&gMenuOption
)) {
2436 ControlFlag
= CfReadKey
;
2440 ASSERT(MenuOption
!= NULL
);
2441 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2442 ScreenOperation
= UiSelect
;
2448 if (((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2449 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2452 // If the function key has been disabled, just ignore the key.
2455 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2456 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2457 if (Key
.ScanCode
== SCAN_F9
) {
2459 // Reset to standard default
2461 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2463 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2472 case CfScreenOperation
:
2473 if (ScreenOperation
!= UiReset
) {
2475 // If the screen has no menu items, and the user didn't select UiReset
2476 // ignore the selection and go back to reading keys.
2478 if (IsListEmpty (&gMenuOption
)) {
2479 ControlFlag
= CfReadKey
;
2483 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2485 for (Link
= gMenuOption
.ForwardLink
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2486 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2487 if (IsSelectable (NextMenuOption
)) {
2492 if (Link
== &gMenuOption
) {
2493 ControlFlag
= CfPrepareToReadKey
;
2499 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2502 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2503 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2510 ControlFlag
= CfCheckSelection
;
2512 ASSERT(MenuOption
!= NULL
);
2513 Statement
= MenuOption
->ThisTag
;
2514 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
2519 // Keep highlight on current MenuOption
2521 Selection
->QuestionId
= Statement
->QuestionId
;
2523 switch (Statement
->Operand
) {
2524 case EFI_IFR_REF_OP
:
2525 if (Statement
->RefDevicePath
!= 0) {
2527 // Goto another Hii Package list
2529 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2531 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2532 if (StringPtr
== NULL
) {
2534 // No device path string not found, exit
2536 Selection
->Action
= UI_ACTION_EXIT
;
2537 Selection
->Statement
= NULL
;
2540 BufferSize
= StrLen (StringPtr
) / 2;
2541 DevicePath
= AllocatePool (BufferSize
);
2542 ASSERT (DevicePath
!= NULL
);
2545 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2547 DevicePathBuffer
= (UINT8
*) DevicePath
;
2548 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
2549 TemStr
[0] = StringPtr
[Index
];
2550 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
2551 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
2553 // Invalid Hex Char as the tail.
2557 if ((Index
& 1) == 0) {
2558 DevicePathBuffer
[Index
/2] = DigitUint8
;
2560 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2564 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2565 if (Selection
->Handle
== NULL
) {
2567 // If target Hii Handle not found, exit
2569 Selection
->Action
= UI_ACTION_EXIT
;
2570 Selection
->Statement
= NULL
;
2574 FreePool (StringPtr
);
2575 FreePool (DevicePath
);
2577 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2578 Selection
->FormId
= Statement
->RefFormId
;
2579 Selection
->QuestionId
= Statement
->RefQuestionId
;
2580 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2582 // Goto another Formset, check for uncommitted data
2584 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2586 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2587 Selection
->FormId
= Statement
->RefFormId
;
2588 Selection
->QuestionId
= Statement
->RefQuestionId
;
2589 } else if (Statement
->RefFormId
!= 0) {
2591 // Check whether target From is suppressed.
2593 RefForm
= IdToForm (Selection
->FormSet
, Statement
->RefFormId
);
2595 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2596 Status
= EvaluateExpression (Selection
->FormSet
, RefForm
, RefForm
->SuppressExpression
);
2597 if (EFI_ERROR (Status
)) {
2601 if (RefForm
->SuppressExpression
->Result
.Value
.b
) {
2603 // Form is suppressed.
2606 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2607 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2615 // Goto another form inside this formset,
2617 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2620 // Link current form so that we can always go back when someone hits the ESC
2622 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Statement
->RefFormId
);
2623 if (MenuList
== NULL
) {
2624 MenuList
= UiAddMenuList (CurrentMenu
, &Selection
->FormSetGuid
, Statement
->RefFormId
);
2627 Selection
->FormId
= Statement
->RefFormId
;
2628 Selection
->QuestionId
= Statement
->RefQuestionId
;
2629 } else if (Statement
->RefQuestionId
!= 0) {
2631 // Goto another Question
2633 Selection
->QuestionId
= Statement
->RefQuestionId
;
2635 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2636 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2645 case EFI_IFR_ACTION_OP
:
2647 // Process the Config string <ConfigResp>
2649 Status
= ProcessQuestionConfig (Selection
, Statement
);
2651 if (EFI_ERROR (Status
)) {
2656 // The action button may change some Question value, so refresh the form
2658 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2661 case EFI_IFR_RESET_BUTTON_OP
:
2663 // Reset Question to default value specified by DefaultId
2665 ControlFlag
= CfUiDefault
;
2666 DefaultId
= Statement
->DefaultId
;
2671 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2673 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
2674 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2676 if (EFI_ERROR (Status
)) {
2679 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2681 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2684 if (OptionString
!= NULL
) {
2685 FreePool (OptionString
);
2693 // We come here when someone press ESC
2695 ControlFlag
= CfCheckSelection
;
2697 if (CurrentMenu
->Parent
!= NULL
) {
2699 // we have a parent, so go to the parent menu
2701 if (CompareGuid (&CurrentMenu
->FormSetGuid
, &CurrentMenu
->Parent
->FormSetGuid
)) {
2703 // The parent menu and current menu are in the same formset
2705 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2707 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2709 Selection
->Statement
= NULL
;
2711 Selection
->FormId
= CurrentMenu
->Parent
->FormId
;
2712 Selection
->QuestionId
= CurrentMenu
->Parent
->QuestionId
;
2715 // Clear highlight record for this menu
2717 CurrentMenu
->QuestionId
= 0;
2721 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
) {
2723 // We never exit FrontPage, so skip the ESC
2725 Selection
->Action
= UI_ACTION_NONE
;
2730 // We are going to leave current FormSet, so check uncommited data in this FormSet
2732 if (gNvUpdateRequired
) {
2733 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2735 YesResponse
= gYesResponse
[0];
2736 NoResponse
= gNoResponse
[0];
2739 // If NV flag is up, prompt user
2742 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveChanges
, gAreYouSure
, gEmptyString
);
2745 (Key
.ScanCode
!= SCAN_ESC
) &&
2746 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2747 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2750 if (Key
.ScanCode
== SCAN_ESC
) {
2752 // User hits the ESC key
2757 Selection
->Action
= UI_ACTION_NONE
;
2762 // If the user hits the YesResponse key
2764 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2765 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2769 Selection
->Action
= UI_ACTION_EXIT
;
2770 Selection
->Statement
= NULL
;
2771 CurrentMenu
->QuestionId
= 0;
2776 ControlFlag
= CfCheckSelection
;
2777 ASSERT(MenuOption
!= NULL
);
2778 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2779 if (MenuOption
->Sequence
!= 0) {
2781 // In the middle or tail of the Date/Time op-code set, go left.
2783 ASSERT(NewPos
!= NULL
);
2784 NewPos
= NewPos
->BackLink
;
2790 ControlFlag
= CfCheckSelection
;
2791 ASSERT(MenuOption
!= NULL
);
2792 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2793 if (MenuOption
->Sequence
!= 2) {
2795 // In the middle or tail of the Date/Time op-code set, go left.
2797 ASSERT(NewPos
!= NULL
);
2798 NewPos
= NewPos
->ForwardLink
;
2804 ControlFlag
= CfCheckSelection
;
2806 SavedListEntry
= TopOfScreen
;
2808 ASSERT(NewPos
!= NULL
);
2809 if (NewPos
->BackLink
!= &gMenuOption
) {
2812 // Adjust Date/Time position before we advance forward.
2814 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2817 // Caution that we have already rewind to the top, don't go backward in this situation.
2819 if (NewPos
->BackLink
!= &gMenuOption
) {
2820 NewPos
= NewPos
->BackLink
;
2823 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2824 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2825 DistanceValue
= PreviousMenuOption
->Skip
;
2828 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2829 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2830 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2831 // checking can be done.
2833 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2835 ASSERT (MenuOption
!= NULL
);
2836 if (Difference
< 0) {
2838 // We want to goto previous MenuOption, but finally we go down.
2839 // it means that we hit the begining MenuOption that can be focused
2840 // so we simply scroll to the top
2842 if (SavedListEntry
!= gMenuOption
.ForwardLink
) {
2843 TopOfScreen
= gMenuOption
.ForwardLink
;
2846 } else if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
- Difference
< (INTN
) TopRow
) {
2848 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2850 TopOfScreen
= NewPos
;
2857 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2859 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2861 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2863 SavedMenuOption
= MenuOption
;
2864 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2865 if (!IsSelectable (MenuOption
)) {
2867 // If we are at the end of the list and sitting on a text op, we need to more forward
2869 ScreenOperation
= UiDown
;
2870 ControlFlag
= CfScreenOperation
;
2874 MenuOption
= SavedMenuOption
;
2879 ControlFlag
= CfCheckSelection
;
2881 ASSERT(NewPos
!= NULL
);
2882 if (NewPos
->BackLink
== &gMenuOption
) {
2891 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2893 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
2894 Index
= Index
- PreviousMenuOption
->Skip
;
2895 Link
= Link
->BackLink
;
2896 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2900 Difference
= MoveToNextStatement (TRUE
, &Link
);
2901 if (Difference
> 0) {
2903 // The focus MenuOption is above the TopOfScreen
2906 } else if (Difference
< 0) {
2908 // This happens when there is no MenuOption can be focused from
2909 // Current MenuOption to the first MenuOption
2911 TopOfScreen
= gMenuOption
.ForwardLink
;
2913 Index
+= Difference
;
2914 if (Index
< TopRow
) {
2918 if (NewPos
== Link
) {
2926 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2927 // Don't do this when we are already in the first page.
2929 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2930 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2934 ControlFlag
= CfCheckSelection
;
2936 ASSERT (NewPos
!= NULL
);
2937 if (NewPos
->ForwardLink
== &gMenuOption
) {
2946 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2948 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
2949 Index
= Index
+ NextMenuOption
->Skip
;
2950 Link
= Link
->ForwardLink
;
2951 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2954 Index
+= MoveToNextStatement (FALSE
, &Link
);
2955 if (Index
> BottomRow
) {
2957 // There are more MenuOption needing scrolling
2962 if (NewPos
== Link
&& Index
<= BottomRow
) {
2964 // Finally we know that NewPos is the last MenuOption can be focused.
2973 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2974 // Don't do this when we are already in the last page.
2976 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2977 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2981 ControlFlag
= CfCheckSelection
;
2983 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2984 // to be one that progresses to the next set of op-codes, we need to advance to the last
2985 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2986 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2987 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2988 // the Date/Time op-code.
2990 SavedListEntry
= NewPos
;
2991 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2993 if (NewPos
->ForwardLink
!= &gMenuOption
) {
2994 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2996 NewPos
= NewPos
->ForwardLink
;
2998 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2999 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3002 // An option might be multi-line, so we need to reflect that data in the overall skip value
3004 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, (UINTN
) SkipValue
);
3005 DistanceValue
+= NextMenuOption
->Skip
;
3007 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3008 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3009 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3010 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3016 // If we are going to scroll, update TopOfScreen
3018 if (Temp
> BottomRow
) {
3021 // Is the current top of screen a zero-advance op-code?
3022 // If so, keep moving forward till we hit a >0 advance op-code
3024 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3027 // If bottom op-code is more than one line or top op-code is more than one line
3029 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3031 // Is the bottom op-code greater than or equal in size to the top op-code?
3033 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3035 // Skip the top op-code
3037 TopOfScreen
= TopOfScreen
->ForwardLink
;
3038 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3040 OldSkipValue
= Difference
;
3042 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3045 // If we have a remainder, skip that many more op-codes until we drain the remainder
3047 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3049 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3051 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3052 TopOfScreen
= TopOfScreen
->ForwardLink
;
3053 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3056 // Since we will act on this op-code in the next routine, and increment the
3057 // SkipValue, set the skips to one less than what is required.
3059 SkipValue
= Difference
- 1;
3063 // Since we will act on this op-code in the next routine, and increment the
3064 // SkipValue, set the skips to one less than what is required.
3066 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3069 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3070 TopOfScreen
= TopOfScreen
->ForwardLink
;
3073 SkipValue
= OldSkipValue
;
3077 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3078 // Let's set a skip flag to smoothly scroll the top of the screen.
3080 if (SavedMenuOption
->Skip
> 1) {
3081 if (SavedMenuOption
== NextMenuOption
) {
3086 } else if (SavedMenuOption
->Skip
== 1) {
3090 TopOfScreen
= TopOfScreen
->ForwardLink
;
3092 } while (SavedMenuOption
->Skip
== 0);
3095 OldSkipValue
= SkipValue
;
3098 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3100 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3103 SavedMenuOption
= MenuOption
;
3104 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3105 if (!IsSelectable (MenuOption
)) {
3107 // If we are at the end of the list and sitting on a text op, we need to more forward
3109 ScreenOperation
= UiUp
;
3110 ControlFlag
= CfScreenOperation
;
3114 MenuOption
= SavedMenuOption
;
3116 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3118 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3123 ControlFlag
= CfCheckSelection
;
3128 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
3130 if (!EFI_ERROR (Status
)) {
3131 ASSERT(MenuOption
!= NULL
);
3132 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3133 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3136 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3137 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3145 ControlFlag
= CfCheckSelection
;
3146 if (!Selection
->FormEditable
) {
3148 // This Form is not editable, ignore the F9 (reset to default)
3153 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
3155 if (!EFI_ERROR (Status
)) {
3156 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3157 Selection
->Statement
= NULL
;
3160 // Show NV update flag on status bar
3162 gNvUpdateRequired
= TRUE
;
3166 case CfUiNoOperation
:
3167 ControlFlag
= CfCheckSelection
;
3171 UiFreeRefreshList ();
3173 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3174 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3175 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3176 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");