2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2008, 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.
20 MENU_REFRESH_ENTRY
*gMenuRefreshHead
;
23 // Search table for UiDisplayMenu()
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
68 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
123 Set Buffer to Value for Size bytes.
125 @param Buffer Memory to set.
126 @param Size Number of bytes to set
127 @param Value Value of the set operation.
142 while ((Size
--) != 0) {
149 Initialize Menu option list.
157 InitializeListHead (&Menu
);
162 Initialize Menu option list.
170 InitializeListHead (&gMenuList
);
175 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
177 @param Selection Menu selection.
181 UiRemoveMenuListEntry (
182 OUT UI_MENU_SELECTION
*Selection
185 UI_MENU_LIST
*UiMenuList
;
187 if (!IsListEmpty (&gMenuList
)) {
188 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
190 Selection
->FormId
= UiMenuList
->FormId
;
191 Selection
->QuestionId
= UiMenuList
->QuestionId
;
192 RemoveEntryList (&UiMenuList
->MenuLink
);
193 gBS
->FreePool (UiMenuList
);
199 Free Menu option linked list.
207 UI_MENU_LIST
*UiMenuList
;
209 while (!IsListEmpty (&gMenuList
)) {
210 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
211 RemoveEntryList (&UiMenuList
->MenuLink
);
212 gBS
->FreePool (UiMenuList
);
218 Add one menu entry to the linked lst
220 @param Selection Menu selection.
225 IN UI_MENU_SELECTION
*Selection
228 UI_MENU_LIST
*UiMenuList
;
230 UiMenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
231 ASSERT (UiMenuList
!= NULL
);
233 UiMenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
234 UiMenuList
->FormId
= Selection
->FormId
;
235 UiMenuList
->QuestionId
= Selection
->QuestionId
;
237 InsertHeadList (&gMenuList
, &UiMenuList
->MenuLink
);
242 Free Menu option linked list.
250 UI_MENU_OPTION
*MenuOption
;
252 while (!IsListEmpty (&Menu
)) {
253 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
254 RemoveEntryList (&MenuOption
->Link
);
257 // We allocated space for this description when we did a GetToken, free it here
259 if (MenuOption
->Skip
!= 0) {
261 // For date/time, MenuOption->Description is shared by three Menu Options
262 // Data format : [01/02/2004] [11:22:33]
263 // Line number : 0 0 1 0 0 1
265 gBS
->FreePool (MenuOption
->Description
);
267 gBS
->FreePool (MenuOption
);
273 Free Menu option linked list.
281 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
283 while (gMenuRefreshHead
!= NULL
) {
284 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
285 gBS
->FreePool (gMenuRefreshHead
);
286 gMenuRefreshHead
= OldMenuRefreshEntry
;
289 gMenuRefreshHead
= NULL
;
303 CHAR16
*OptionString
;
304 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
307 UI_MENU_SELECTION
*Selection
;
308 FORM_BROWSER_STATEMENT
*Question
;
309 EFI_HII_CONFIG_ACCESS_PROTOCOL
*ConfigAccess
;
310 EFI_HII_VALUE
*HiiValue
;
311 EFI_BROWSER_ACTION_REQUEST ActionRequest
;
313 if (gMenuRefreshHead
!= NULL
) {
315 MenuRefreshEntry
= gMenuRefreshHead
;
318 // Reset FormPackage update flag
320 mHiiPackageListUpdated
= FALSE
;
323 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
325 Selection
= MenuRefreshEntry
->Selection
;
326 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
328 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
329 if (EFI_ERROR (Status
)) {
334 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
336 if (OptionString
!= NULL
) {
338 // If leading spaces on OptionString - remove the spaces
340 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
343 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, &OptionString
[Index
]);
344 gBS
->FreePool (OptionString
);
348 // Question value may be changed, need invoke its Callback()
350 ConfigAccess
= Selection
->FormSet
->ConfigAccess
;
351 if ((Question
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) && (ConfigAccess
!= NULL
)) {
352 ActionRequest
= EFI_BROWSER_ACTION_REQUEST_NONE
;
354 HiiValue
= &Question
->HiiValue
;
355 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
357 // Create String in HII database for Configuration Driver to retrieve
359 HiiValue
->Value
.string
= NewString ((CHAR16
*) Question
->BufferValue
, Selection
->FormSet
->HiiHandle
);
362 Status
= ConfigAccess
->Callback (
364 EFI_BROWSER_ACTION_CHANGING
,
365 Question
->QuestionId
,
371 if (HiiValue
->Type
== EFI_IFR_TYPE_STRING
) {
373 // Clean the String in HII Database
375 DeleteString (HiiValue
->Value
.string
, Selection
->FormSet
->HiiHandle
);
378 if (!EFI_ERROR (Status
)) {
379 switch (ActionRequest
) {
380 case EFI_BROWSER_ACTION_REQUEST_RESET
:
381 gResetRequired
= TRUE
;
384 case EFI_BROWSER_ACTION_REQUEST_SUBMIT
:
385 SubmitForm (Selection
->FormSet
, Selection
->Form
);
388 case EFI_BROWSER_ACTION_REQUEST_EXIT
:
389 Selection
->Action
= UI_ACTION_EXIT
;
390 gNvUpdateRequired
= FALSE
;
399 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
401 } while (MenuRefreshEntry
!= NULL
);
403 if (mHiiPackageListUpdated
) {
405 // Package list is updated, force to reparse IFR binary of target Formset
407 mHiiPackageListUpdated
= FALSE
;
408 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
418 Wait for a given event to fire, or for an optional timeout to expire.
420 @param Event The event to wait for
421 @param Timeout An optional timeout value in 100 ns units.
422 @param RefreshInterval Menu refresh interval (in seconds).
424 @retval EFI_SUCCESS Event fired before Timeout expired.
425 @retval EFI_TIME_OUT Timout expired before Event fired.
429 UiWaitForSingleEvent (
431 IN UINT64 Timeout
, OPTIONAL
432 IN UINT8 RefreshInterval OPTIONAL
437 EFI_EVENT TimerEvent
;
438 EFI_EVENT WaitList
[2];
442 // Create a timer event
444 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
445 if (!EFI_ERROR (Status
)) {
447 // Set the timer event
456 // Wait for the original event or the timer
459 WaitList
[1] = TimerEvent
;
460 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
461 gBS
->CloseEvent (TimerEvent
);
464 // If the timer expired, change the return to timed out
466 if (!EFI_ERROR (Status
) && Index
== 1) {
467 Status
= EFI_TIMEOUT
;
472 // Update screen every second
474 if (RefreshInterval
== 0) {
475 Timeout
= ONE_SECOND
;
477 Timeout
= RefreshInterval
* ONE_SECOND
;
481 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
484 // Set the timer event
493 // Wait for the original event or the timer
496 WaitList
[1] = TimerEvent
;
497 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
500 // If the timer expired, update anything that needs a refresh and keep waiting
502 if (!EFI_ERROR (Status
) && Index
== 1) {
503 Status
= EFI_TIMEOUT
;
504 if (RefreshInterval
!= 0) {
505 Status
= RefreshForm ();
509 gBS
->CloseEvent (TimerEvent
);
510 } while (Status
== EFI_TIMEOUT
);
518 Add one menu option by specified description and context.
520 @param String String description for this option.
521 @param Handle Hii handle for the package list.
522 @param Statement Statement of this Menu Option.
523 @param NumberOfLines Display lines for this Menu Option.
524 @param MenuItemCount The index for this Option in the Menu.
530 IN EFI_HII_HANDLE Handle
,
531 IN FORM_BROWSER_STATEMENT
*Statement
,
532 IN UINT16 NumberOfLines
,
533 IN UINT16 MenuItemCount
536 UI_MENU_OPTION
*MenuOption
;
542 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
544 // Add three MenuOptions for Date/Time
545 // Data format : [01/02/2004] [11:22:33]
546 // Line number : 0 0 1 0 0 1
551 if (Statement
->Storage
== NULL
) {
553 // For RTC type of date/time, set default refresh interval to be 1 second
555 if (Statement
->RefreshInterval
== 0) {
556 Statement
->RefreshInterval
= 1;
561 for (Index
= 0; Index
< Count
; Index
++) {
562 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
565 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
566 MenuOption
->Description
= String
;
567 MenuOption
->Handle
= Handle
;
568 MenuOption
->ThisTag
= Statement
;
569 MenuOption
->EntryNumber
= MenuItemCount
;
573 // Override LineNumber for the MenuOption in Date/Time sequence
575 MenuOption
->Skip
= 1;
577 MenuOption
->Skip
= NumberOfLines
;
579 MenuOption
->Sequence
= Index
;
581 if (Statement
->GrayOutExpression
!= NULL
) {
582 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
585 if ((Statement
->ValueExpression
!= NULL
) ||
586 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
587 MenuOption
->ReadOnly
= TRUE
;
590 InsertTailList (&Menu
, &MenuOption
->Link
);
596 Routine used to abstract a generic dialog interface and return the selected key or string
598 @param NumberOfLines The number of lines for the dialog box
599 @param HotKey Defines whether a single character is parsed
600 (TRUE) and returned in KeyValue or a string is
601 returned in StringBuffer. Two special characters
602 are considered when entering a string, a SCAN_ESC
603 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
604 string input and returns
605 @param MaximumStringSize The maximum size in bytes of a typed in string
606 (each character is a CHAR16) and the minimum
607 string returned is two bytes
608 @param StringBuffer The passed in pointer to the buffer which will
609 hold the typed in string if HotKey is FALSE
610 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
611 @param ... A series of (quantity == NumberOfLines) text
612 strings which will be used to construct the dialog
615 @retval EFI_SUCCESS Displayed dialog and received user interaction
616 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
617 (StringBuffer == NULL) && (HotKey == FALSE))
618 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
623 IN UINTN NumberOfLines
,
625 IN UINTN MaximumStringSize
,
626 OUT CHAR16
*StringBuffer
,
627 OUT EFI_INPUT_KEY
*KeyValue
,
632 VA_LIST MarkerBackup
;
637 CHAR16
*BufferedString
;
644 BOOLEAN SelectionComplete
;
646 UINTN CurrentAttribute
;
647 UINTN DimensionsWidth
;
648 UINTN DimensionsHeight
;
650 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
651 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
653 SelectionComplete
= FALSE
;
655 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
656 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
657 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
660 ASSERT (BufferedString
);
662 VA_START (Marker
, KeyValue
);
663 MarkerBackup
= Marker
;
666 // Zero the outgoing buffer
668 ZeroMem (StringBuffer
, MaximumStringSize
);
671 if (KeyValue
== NULL
) {
672 return EFI_INVALID_PARAMETER
;
675 if (StringBuffer
== NULL
) {
676 return EFI_INVALID_PARAMETER
;
682 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
687 // Determine the largest string in the dialog box
688 // Notice we are starting with 1 since String is the first string
690 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
691 StackString
= VA_ARG (Marker
, CHAR16
*);
693 if (StackString
[0] == L
' ') {
694 InputOffset
= Count
+ 1;
697 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
699 // Size of the string visually and subtract the width by one for the null-terminator
701 LargestString
= (GetStringWidth (StackString
) / 2);
705 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
706 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
713 CreateSharedPopUp (LargestString
, NumberOfLines
, MarkerBackup
);
716 // Take the first key typed and report it back?
719 Status
= WaitForKeyStroke (&Key
);
720 ASSERT_EFI_ERROR (Status
);
721 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
725 Status
= WaitForKeyStroke (&Key
);
727 switch (Key
.UnicodeChar
) {
729 switch (Key
.ScanCode
) {
731 gBS
->FreePool (TempString
);
732 gBS
->FreePool (BufferedString
);
733 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
734 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
735 return EFI_DEVICE_ERROR
;
743 case CHAR_CARRIAGE_RETURN
:
744 SelectionComplete
= TRUE
;
745 gBS
->FreePool (TempString
);
746 gBS
->FreePool (BufferedString
);
747 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
748 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
753 if (StringBuffer
[0] != CHAR_NULL
) {
754 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
755 TempString
[Index
] = StringBuffer
[Index
];
758 // Effectively truncate string by 1 character
760 TempString
[Index
- 1] = CHAR_NULL
;
761 StrCpy (StringBuffer
, TempString
);
766 // If it is the beginning of the string, don't worry about checking maximum limits
768 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
769 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
770 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
771 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
772 KeyPad
[0] = Key
.UnicodeChar
;
773 KeyPad
[1] = CHAR_NULL
;
774 StrCat (StringBuffer
, KeyPad
);
775 StrCat (TempString
, KeyPad
);
778 // If the width of the input string is now larger than the screen, we nee to
779 // adjust the index to start printing portions of the string
781 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
783 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
785 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
786 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
791 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
792 BufferedString
[Count
] = StringBuffer
[Index
];
795 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
798 } while (!SelectionComplete
);
801 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
802 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
807 Draw a pop up windows based on the dimension, number of lines and
810 @param RequestedWidth The width of the pop-up.
811 @param NumberOfLines The number of lines.
812 @param Marker The variable argument list for the list of string to be printed.
817 IN UINTN RequestedWidth
,
818 IN UINTN NumberOfLines
,
830 UINTN DimensionsWidth
;
831 UINTN DimensionsHeight
;
833 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
834 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
836 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
838 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
839 RequestedWidth
= DimensionsWidth
- 2;
843 // Subtract the PopUp width from total Columns, allow for one space extra on
844 // each end plus a border.
846 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
847 End
= Start
+ RequestedWidth
+ 1;
849 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
850 Bottom
= Top
+ NumberOfLines
+ 2;
852 Character
= BOXDRAW_DOWN_RIGHT
;
853 PrintCharAt (Start
, Top
, Character
);
854 Character
= BOXDRAW_HORIZONTAL
;
855 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
856 PrintChar (Character
);
859 Character
= BOXDRAW_DOWN_LEFT
;
860 PrintChar (Character
);
861 Character
= BOXDRAW_VERTICAL
;
864 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
865 String
= VA_ARG (Marker
, CHAR16
*);
868 // This will clear the background of the line - we never know who might have been
869 // here before us. This differs from the next clear in that it used the non-reverse
870 // video for normal printing.
872 if (GetStringWidth (String
) / 2 > 1) {
873 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
877 // Passing in a space results in the assumption that this is where typing will occur
879 if (String
[0] == L
' ') {
880 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
884 // Passing in a NULL results in a blank space
886 if (String
[0] == CHAR_NULL
) {
887 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
891 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
895 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
896 PrintCharAt (Start
, Index
+ 1, Character
);
897 PrintCharAt (End
- 1, Index
+ 1, Character
);
900 Character
= BOXDRAW_UP_RIGHT
;
901 PrintCharAt (Start
, Bottom
- 1, Character
);
902 Character
= BOXDRAW_HORIZONTAL
;
903 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
904 PrintChar (Character
);
907 Character
= BOXDRAW_UP_LEFT
;
908 PrintChar (Character
);
912 Draw a pop up windows based on the dimension, number of lines and
915 @param RequestedWidth The width of the pop-up.
916 @param NumberOfLines The number of lines.
917 @param ... A series of text strings that displayed in the pop-up.
922 IN UINTN RequestedWidth
,
923 IN UINTN NumberOfLines
,
929 VA_START (Marker
, NumberOfLines
);
931 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
938 Update status bar on the bottom of menu.
940 @param MessageType The type of message to be shown.
941 @param Flags The flags in Question header.
942 @param State Set or clear.
947 IN UINTN MessageType
,
953 CHAR16
*NvUpdateMessage
;
954 CHAR16
*InputErrorMessage
;
956 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
957 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
959 switch (MessageType
) {
962 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
964 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
965 gScreenDimensions
.BottomRow
- 1,
970 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
971 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
972 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
979 case NV_UPDATE_REQUIRED
:
980 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
982 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
984 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
985 gScreenDimensions
.BottomRow
- 1,
988 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
990 gNvUpdateRequired
= TRUE
;
992 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
993 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
995 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
996 gScreenDimensions
.BottomRow
- 1,
1001 gNvUpdateRequired
= FALSE
;
1006 case REFRESH_STATUS_BAR
:
1008 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
1011 if (gNvUpdateRequired
) {
1012 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1020 gBS
->FreePool (InputErrorMessage
);
1021 gBS
->FreePool (NvUpdateMessage
);
1027 Get the supported width for a particular op-code
1029 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1030 @param Handle The handle in the HII database being used
1032 @return Returns the number of CHAR16 characters that is support.
1037 IN FORM_BROWSER_STATEMENT
*Statement
,
1038 IN EFI_HII_HANDLE Handle
1048 // See if the second text parameter is really NULL
1050 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1051 String
= GetToken (Statement
->TextTwo
, Handle
);
1052 Size
= StrLen (String
);
1053 gBS
->FreePool (String
);
1056 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1057 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1058 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1059 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1060 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1062 // Allow a wide display if text op-code and no secondary text op-code
1064 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1066 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1068 Width
= (UINT16
) gPromptBlockWidth
;
1071 if (Statement
->InSubtitle
) {
1072 Width
-= SUBTITLE_INDENT
;
1079 BOOLEAN GetLineByWidthFinished
= FALSE
;
1082 Will copy LineWidth amount of a string in the OutputString buffer and return the
1083 number of CHAR16 characters that were copied into the OutputString buffer.
1085 @param InputString String description for this option.
1086 @param LineWidth Width of the desired string to extract in CHAR16
1088 @param Index Where in InputString to start the copy process
1089 @param OutputString Buffer to copy the string into
1091 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1096 IN CHAR16
*InputString
,
1097 IN UINT16 LineWidth
,
1098 IN OUT UINTN
*Index
,
1099 OUT CHAR16
**OutputString
1105 if (GetLineByWidthFinished
) {
1106 GetLineByWidthFinished
= FALSE
;
1113 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1116 // Ensure we have got a valid buffer
1118 if (*OutputString
!= NULL
) {
1121 //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.
1122 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1124 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1125 *Index
= *Index
+ 2;
1129 // Fast-forward the string and see if there is a carriage-return in the string
1131 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1135 // Copy the desired LineWidth of data to the output buffer.
1136 // Also make sure that we don't copy more than the string.
1137 // Also make sure that if there are linefeeds, we account for them.
1139 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1140 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1143 // Convert to CHAR16 value and show that we are done with this operation
1145 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1146 if (LineWidth
!= 0) {
1147 GetLineByWidthFinished
= TRUE
;
1150 if (Count2
== LineWidth
) {
1152 // Rewind the string from the maximum size until we see a space to break the line
1154 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1156 if (LineWidth
== 0) {
1164 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1167 // If currently pointing to a space, increment the index to the first non-space character
1170 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1174 *Index
= (UINT16
) (*Index
+ LineWidth
);
1183 Update display lines for a Menu Option.
1185 @param Selection The user's selection.
1186 @param MenuOption The MenuOption to be checked.
1187 @param OptionalString The option string.
1188 @param SkipValue The number of lins to skip.
1192 UpdateOptionSkipLines (
1193 IN UI_MENU_SELECTION
*Selection
,
1194 IN UI_MENU_OPTION
*MenuOption
,
1195 OUT CHAR16
**OptionalString
,
1203 CHAR16
*OutputString
;
1204 CHAR16
*OptionString
;
1207 OptionString
= *OptionalString
;
1208 OutputString
= NULL
;
1210 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1212 if (OptionString
!= NULL
) {
1213 Width
= (UINT16
) gOptionBlockWidth
;
1217 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1219 // If there is more string to process print on the next row and increment the Skip value
1221 if (StrLen (&OptionString
[Index
])) {
1222 if (SkipValue
== 0) {
1225 // Since the Number of lines for this menu entry may or may not be reflected accurately
1226 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1227 // some testing to ensure we are keeping this in-sync.
1229 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1231 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1237 gBS
->FreePool (OutputString
);
1238 if (SkipValue
!= 0) {
1246 *OptionalString
= OptionString
;
1251 Check whether this Menu Option could be highlighted.
1253 This is an internal function.
1255 @param MenuOption The MenuOption to be checked.
1257 @retval TRUE This Menu Option is selectable.
1258 @retval FALSE This Menu Option could not be selected.
1263 UI_MENU_OPTION
*MenuOption
1266 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1267 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1276 Determine if the menu is the last menu that can be selected.
1278 This is an internal function.
1280 @param Direction The scroll direction. False is down. True is up.
1281 @param CurrentPos The current focus.
1283 @return FALSE -- the menu isn't the last menu that can be selected.
1284 @return TRUE -- the menu is the last menu that can be selected.
1289 IN BOOLEAN Direction
,
1290 IN LIST_ENTRY
*CurrentPos
1294 UI_MENU_OPTION
*MenuOption
;
1296 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1298 if (Temp
== &Menu
) {
1302 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1303 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1304 if (IsSelectable (MenuOption
)) {
1314 Move to next selectable statement.
1316 This is an internal function.
1318 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1319 @param CurrentPosition Current position.
1321 @return The row distance from current MenuOption to next selectable MenuOption.
1325 MoveToNextStatement (
1327 IN OUT LIST_ENTRY
**CurrentPosition
1333 UI_MENU_OPTION
*NextMenuOption
;
1336 Pos
= *CurrentPosition
;
1340 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1341 if (IsSelectable (NextMenuOption
)) {
1344 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1348 Distance
+= NextMenuOption
->Skip
;
1349 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1354 // If we hit end there is still no statement can be focused,
1355 // we go backwards to find the statement can be focused.
1358 Pos
= *CurrentPosition
;
1361 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1362 if (IsSelectable (NextMenuOption
)) {
1365 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1369 Distance
-= NextMenuOption
->Skip
;
1370 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1374 *CurrentPosition
= &NextMenuOption
->Link
;
1380 Adjust Data and Time position accordingly.
1381 Data format : [01/02/2004] [11:22:33]
1382 Line number : 0 0 1 0 0 1
1384 This is an internal function.
1386 @param DirectionUp the up or down direction. False is down. True is
1388 @param CurrentPosition Current position. On return: Point to the last
1389 Option (Year or Second) if up; Point to the first
1390 Option (Month or Hour) if down.
1392 @return Return line number to pad. It is possible that we stand on a zero-advance
1393 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1397 AdjustDateAndTimePosition (
1398 IN BOOLEAN DirectionUp
,
1399 IN OUT LIST_ENTRY
**CurrentPosition
1403 LIST_ENTRY
*NewPosition
;
1404 UI_MENU_OPTION
*MenuOption
;
1405 UINTN PadLineNumber
;
1408 NewPosition
= *CurrentPosition
;
1409 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1411 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1412 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1414 // Calculate the distance from current position to the last Date/Time MenuOption
1417 while (MenuOption
->Skip
== 0) {
1419 NewPosition
= NewPosition
->ForwardLink
;
1420 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1424 NewPosition
= *CurrentPosition
;
1427 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1428 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1429 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1430 // checking can be done.
1432 while (Count
++ < 2) {
1433 NewPosition
= NewPosition
->BackLink
;
1437 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1438 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1439 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1440 // checking can be done.
1442 while (Count
-- > 0) {
1443 NewPosition
= NewPosition
->ForwardLink
;
1447 *CurrentPosition
= NewPosition
;
1450 return PadLineNumber
;
1455 Display menu and wait for user to select one menu option, then return it.
1456 If AutoBoot is enabled, then if user doesn't select any option,
1457 after period of time, it will automatically return the first menu option.
1459 @param Selection Menu selection.
1461 @retval EFI_SUCESSS This function always return successfully for now.
1466 IN OUT UI_MENU_SELECTION
*Selection
1472 UINTN DistanceValue
;
1484 CHAR16
*OptionString
;
1485 CHAR16
*OutputString
;
1486 CHAR16
*FormattedString
;
1496 LIST_ENTRY
*TopOfScreen
;
1497 LIST_ENTRY
*SavedListEntry
;
1498 UI_MENU_OPTION
*MenuOption
;
1499 UI_MENU_OPTION
*NextMenuOption
;
1500 UI_MENU_OPTION
*SavedMenuOption
;
1501 UI_MENU_OPTION
*PreviousMenuOption
;
1502 UI_CONTROL_FLAG ControlFlag
;
1503 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1504 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1505 UI_SCREEN_OPERATION ScreenOperation
;
1506 UINT8 MinRefreshInterval
;
1509 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1510 FORM_BROWSER_STATEMENT
*Statement
;
1512 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1514 Status
= EFI_SUCCESS
;
1515 FormattedString
= NULL
;
1516 OptionString
= NULL
;
1517 ScreenOperation
= UiNoOperation
;
1519 MinRefreshInterval
= 0;
1522 OutputString
= NULL
;
1527 MenuRefreshEntry
= gMenuRefreshHead
;
1529 NextMenuOption
= NULL
;
1530 PreviousMenuOption
= NULL
;
1531 SavedMenuOption
= NULL
;
1533 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1535 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
1536 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1537 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1539 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1540 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1543 Col
= LocalScreen
.LeftColumn
;
1544 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1546 Selection
->TopRow
= TopRow
;
1547 Selection
->BottomRow
= BottomRow
;
1548 Selection
->PromptCol
= Col
;
1549 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1550 Selection
->Statement
= NULL
;
1552 TopOfScreen
= Menu
.ForwardLink
;
1557 // Get user's selection
1559 NewPos
= Menu
.ForwardLink
;
1561 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1562 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1564 ControlFlag
= CfInitialization
;
1565 Selection
->Action
= UI_ACTION_NONE
;
1567 switch (ControlFlag
) {
1568 case CfInitialization
:
1569 if (IsListEmpty (&Menu
)) {
1570 ControlFlag
= CfReadKey
;
1572 ControlFlag
= CfCheckSelection
;
1576 case CfCheckSelection
:
1577 if (Selection
->Action
!= UI_ACTION_NONE
) {
1578 ControlFlag
= CfExit
;
1580 ControlFlag
= CfRepaint
;
1585 ControlFlag
= CfRefreshHighLight
;
1599 LocalScreen
.LeftColumn
,
1600 LocalScreen
.RightColumn
,
1601 TopRow
- SCROLL_ARROW_HEIGHT
,
1602 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1603 FIELD_TEXT
| FIELD_BACKGROUND
1606 UiFreeRefreshList ();
1607 MinRefreshInterval
= 0;
1609 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1610 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1611 MenuOption
->Row
= Row
;
1612 MenuOption
->Col
= Col
;
1613 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1615 Statement
= MenuOption
->ThisTag
;
1616 if (Statement
->InSubtitle
) {
1617 MenuOption
->Col
+= SUBTITLE_INDENT
;
1620 if (MenuOption
->GrayOut
) {
1621 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1623 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1624 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1628 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1631 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1632 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1633 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1636 // If there is more string to process print on the next row and increment the Skip value
1638 if (StrLen (&MenuOption
->Description
[Index
])) {
1644 gBS
->FreePool (OutputString
);
1653 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1654 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1655 if (EFI_ERROR (Status
)) {
1657 // Repaint to clear possible error prompt pop-up
1661 ControlFlag
= CfRepaint
;
1665 if (OptionString
!= NULL
) {
1666 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1668 // If leading spaces on OptionString - remove the spaces
1670 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1671 MenuOption
->OptCol
++;
1674 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1675 OptionString
[Count
] = OptionString
[Index
];
1679 OptionString
[Count
] = CHAR_NULL
;
1683 // If Question request refresh, register the op-code
1685 if (Statement
->RefreshInterval
!= 0) {
1687 // Menu will be refreshed at minimal interval of all Questions
1688 // which have refresh request
1690 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1691 MinRefreshInterval
= Statement
->RefreshInterval
;
1694 if (gMenuRefreshHead
== NULL
) {
1695 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1696 ASSERT (MenuRefreshEntry
!= NULL
);
1697 MenuRefreshEntry
->MenuOption
= MenuOption
;
1698 MenuRefreshEntry
->Selection
= Selection
;
1699 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1700 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1701 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1702 gMenuRefreshHead
= MenuRefreshEntry
;
1705 // Advance to the last entry
1707 for (MenuRefreshEntry
= gMenuRefreshHead
;
1708 MenuRefreshEntry
->Next
!= NULL
;
1709 MenuRefreshEntry
= MenuRefreshEntry
->Next
1712 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1713 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1714 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1715 MenuRefreshEntry
->MenuOption
= MenuOption
;
1716 MenuRefreshEntry
->Selection
= Selection
;
1717 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1718 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1719 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1723 Width
= (UINT16
) gOptionBlockWidth
;
1726 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1727 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1728 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1731 // If there is more string to process print on the next row and increment the Skip value
1733 if (StrLen (&OptionString
[Index
])) {
1737 // Since the Number of lines for this menu entry may or may not be reflected accurately
1738 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1739 // some testing to ensure we are keeping this in-sync.
1741 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1743 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1749 gBS
->FreePool (OutputString
);
1758 gBS
->FreePool (OptionString
);
1761 // If this is a text op with secondary text information
1763 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1764 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1766 Width
= (UINT16
) gOptionBlockWidth
;
1769 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1770 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1771 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1774 // If there is more string to process print on the next row and increment the Skip value
1776 if (StrLen (&StringPtr
[Index
])) {
1780 // Since the Number of lines for this menu entry may or may not be reflected accurately
1781 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1782 // some testing to ensure we are keeping this in-sync.
1784 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1786 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1792 gBS
->FreePool (OutputString
);
1799 gBS
->FreePool (StringPtr
);
1803 // Need to handle the bottom of the display
1805 if (MenuOption
->Skip
> 1) {
1806 Row
+= MenuOption
->Skip
- SkipValue
;
1809 Row
+= MenuOption
->Skip
;
1812 if (Row
> BottomRow
) {
1813 if (!ValueIsScroll (FALSE
, Link
)) {
1817 Row
= BottomRow
+ 1;
1822 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
1827 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1829 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1830 TopRow
- SCROLL_ARROW_HEIGHT
,
1834 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1838 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1840 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1841 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1845 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1852 case CfRefreshHighLight
:
1854 // MenuOption: Last menu option that need to remove hilight
1855 // MenuOption is set to NULL in Repaint
1856 // NewPos: Current menu option that need to hilight
1858 ControlFlag
= CfUpdateHelpString
;
1861 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1862 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1864 SavedValue
= Repaint
;
1867 if (Selection
->QuestionId
!= 0) {
1868 NewPos
= Menu
.ForwardLink
;
1869 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1871 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
1872 NewPos
= NewPos
->ForwardLink
;
1873 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1875 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
1877 // Target Question found, find its MenuOption
1881 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
1882 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1883 Index
+= SavedMenuOption
->Skip
;
1884 Link
= Link
->ForwardLink
;
1887 if (Link
!= NewPos
|| Index
> BottomRow
) {
1889 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1892 for (Index
= TopRow
; Index
<= BottomRow
; ) {
1893 Link
= Link
->BackLink
;
1894 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1895 Index
+= SavedMenuOption
->Skip
;
1897 TopOfScreen
= Link
->ForwardLink
;
1901 ControlFlag
= CfRepaint
;
1906 // Target Question not found, highlight the default menu option
1908 NewPos
= TopOfScreen
;
1911 Selection
->QuestionId
= 0;
1914 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
1915 if (MenuOption
!= NULL
) {
1917 // Remove highlight on last Menu Option
1919 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1920 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1921 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1922 if (OptionString
!= NULL
) {
1923 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1924 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
1927 // If leading spaces on OptionString - remove the spaces
1929 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1932 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1933 OptionString
[Count
] = OptionString
[Index
];
1937 OptionString
[Count
] = CHAR_NULL
;
1940 Width
= (UINT16
) gOptionBlockWidth
;
1941 OriginalRow
= MenuOption
->Row
;
1943 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1944 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1945 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1948 // If there is more string to process print on the next row and increment the Skip value
1950 if (StrLen (&OptionString
[Index
])) {
1954 gBS
->FreePool (OutputString
);
1957 MenuOption
->Row
= OriginalRow
;
1959 gBS
->FreePool (OptionString
);
1962 if (MenuOption
->GrayOut
) {
1963 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1964 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1965 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1968 OriginalRow
= MenuOption
->Row
;
1969 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
1971 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1972 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1973 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1976 // If there is more string to process print on the next row and increment the Skip value
1978 if (StrLen (&MenuOption
->Description
[Index
])) {
1982 gBS
->FreePool (OutputString
);
1985 MenuOption
->Row
= OriginalRow
;
1986 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1992 // This is only possible if we entered this page and the first menu option is
1993 // a "non-menu" item. In that case, force it UiDown
1995 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1996 if (!IsSelectable (MenuOption
)) {
1997 ASSERT (ScreenOperation
== UiNoOperation
);
1998 ScreenOperation
= UiDown
;
1999 ControlFlag
= CfScreenOperation
;
2004 // This is the current selected statement
2006 Statement
= MenuOption
->ThisTag
;
2007 Selection
->Statement
= Statement
;
2010 // Set reverse attribute
2012 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
2013 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2016 // Assuming that we have a refresh linked-list created, lets annotate the
2017 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2018 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2020 if (gMenuRefreshHead
!= NULL
) {
2021 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2022 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
2023 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2024 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
2029 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2030 if (OptionString
!= NULL
) {
2031 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2033 // If leading spaces on OptionString - remove the spaces
2035 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2038 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2039 OptionString
[Count
] = OptionString
[Index
];
2043 OptionString
[Count
] = CHAR_NULL
;
2045 Width
= (UINT16
) gOptionBlockWidth
;
2047 OriginalRow
= MenuOption
->Row
;
2049 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2050 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2051 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2054 // If there is more string to process print on the next row and increment the Skip value
2056 if (StrLen (&OptionString
[Index
])) {
2060 gBS
->FreePool (OutputString
);
2063 MenuOption
->Row
= OriginalRow
;
2065 gBS
->FreePool (OptionString
);
2068 OriginalRow
= MenuOption
->Row
;
2070 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2072 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2073 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2074 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2077 // If there is more string to process print on the next row and increment the Skip value
2079 if (StrLen (&MenuOption
->Description
[Index
])) {
2083 gBS
->FreePool (OutputString
);
2086 MenuOption
->Row
= OriginalRow
;
2091 UpdateKeyHelp (MenuOption
, FALSE
);
2094 // Clear reverse attribute
2096 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2099 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2100 // if we didn't break halfway when process CfRefreshHighLight.
2102 Repaint
= SavedValue
;
2105 case CfUpdateHelpString
:
2106 ControlFlag
= CfPrepareToReadKey
;
2108 if ((Repaint
|| NewLine
) && (gClassOfVfr
!= EFI_GENERAL_APPLICATION_SUBCLASS
)) {
2110 // Don't print anything if it is a NULL help token
2112 if (MenuOption
->ThisTag
->Help
== 0) {
2115 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2118 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2120 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2122 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2124 // Pad String with spaces to simulate a clearing of the previous line
2126 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2127 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2131 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2133 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2138 // Reset this flag every time we finish using it.
2144 case CfPrepareToReadKey
:
2145 ControlFlag
= CfReadKey
;
2146 ScreenOperation
= UiNoOperation
;
2150 ControlFlag
= CfScreenOperation
;
2153 // Wait for user's selection
2156 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2157 } while (Status
== EFI_TIMEOUT
);
2159 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2161 // IFR is updated in Callback of refresh opcode, re-parse it
2163 ControlFlag
= CfUiReset
;
2164 Selection
->Statement
= NULL
;
2168 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2170 // if we encounter error, continue to read another key in.
2172 if (EFI_ERROR (Status
)) {
2173 ControlFlag
= CfReadKey
;
2177 if (IsListEmpty (&Menu
) && Key
.UnicodeChar
!= CHAR_NULL
) {
2179 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2184 switch (Key
.UnicodeChar
) {
2185 case CHAR_CARRIAGE_RETURN
:
2186 ScreenOperation
= UiSelect
;
2191 // We will push the adjustment of these numeric values directly to the input handler
2192 // NOTE: we won't handle manual input numeric
2196 Statement
= MenuOption
->ThisTag
;
2197 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2198 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2199 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2201 if (Key
.UnicodeChar
== '+') {
2202 gDirection
= SCAN_RIGHT
;
2204 gDirection
= SCAN_LEFT
;
2206 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2207 if (EFI_ERROR (Status
)) {
2209 // Repaint to clear possible error prompt pop-up
2214 if (OptionString
!= NULL
) {
2215 FreePool (OptionString
);
2221 ScreenOperation
= UiUp
;
2226 ScreenOperation
= UiDown
;
2230 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
2231 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2232 ScreenOperation
= UiSelect
;
2238 if (((Key
.ScanCode
== SCAN_F1
) && ((gFunctionKeySetting
& FUNCTION_ONE
) != FUNCTION_ONE
)) ||
2239 ((Key
.ScanCode
== SCAN_F2
) && ((gFunctionKeySetting
& FUNCTION_TWO
) != FUNCTION_TWO
)) ||
2240 ((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2241 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2244 // If the function key has been disabled, just ignore the key.
2247 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2248 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2249 if (Key
.ScanCode
== SCAN_F9
) {
2251 // Reset to standard default
2253 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2255 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2264 case CfScreenOperation
:
2265 if (ScreenOperation
!= UiPrevious
&& ScreenOperation
!= UiReset
) {
2267 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2268 // ignore the selection and go back to reading keys.
2270 if (IsListEmpty (&Menu
)) {
2271 ControlFlag
= CfReadKey
;
2275 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2277 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2278 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2279 if (IsSelectable (NextMenuOption
)) {
2284 if (Link
== &Menu
) {
2285 ControlFlag
= CfPrepareToReadKey
;
2288 } else if (ScreenOperation
== UiReset
) {
2290 // Press ESC to exit FormSet
2292 Selection
->Action
= UI_ACTION_EXIT
;
2293 Selection
->Statement
= NULL
;
2297 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2300 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2301 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2308 ControlFlag
= CfCheckSelection
;
2310 if (IsListEmpty (&gMenuList
)) {
2311 Selection
->Action
= UI_ACTION_NONE
;
2312 if (IsListEmpty (&Menu
)) {
2313 ControlFlag
= CfReadKey
;
2319 // Remove the Cached page entry
2321 UiRemoveMenuListEntry (Selection
);
2323 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2324 Selection
->Statement
= NULL
;
2328 ControlFlag
= CfCheckSelection
;
2330 Statement
= MenuOption
->ThisTag
;
2331 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2332 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2333 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2334 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2339 // Keep highlight on current MenuOption
2341 Selection
->QuestionId
= Statement
->QuestionId
;
2343 switch (Statement
->Operand
) {
2344 case EFI_IFR_REF_OP
:
2345 if (Statement
->RefDevicePath
!= 0) {
2347 // Goto another Hii Package list
2349 ControlFlag
= CfUiReset
;
2350 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2352 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2353 if (StringPtr
== NULL
) {
2355 // No device path string not found, exit
2357 Selection
->Action
= UI_ACTION_EXIT
;
2358 Selection
->Statement
= NULL
;
2361 BufferSize
= StrLen (StringPtr
) / 2;
2362 DevicePath
= AllocatePool (BufferSize
);
2364 HexStringToBufInReverseOrder ((UINT8
*) DevicePath
, &BufferSize
, StringPtr
);
2365 Selection
->Handle
= HiiLibDevicePathToHiiHandle (DevicePath
);
2366 if (Selection
->Handle
== NULL
) {
2368 // If target Hii Handle not found, exit
2370 Selection
->Action
= UI_ACTION_EXIT
;
2371 Selection
->Statement
= NULL
;
2375 gBS
->FreePool (StringPtr
);
2376 gBS
->FreePool (DevicePath
);
2378 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2379 Selection
->FormId
= Statement
->RefFormId
;
2380 Selection
->QuestionId
= Statement
->RefQuestionId
;
2381 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2383 // Goto another Formset, check for uncommitted data
2385 ControlFlag
= CfUiReset
;
2386 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2388 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2389 Selection
->FormId
= Statement
->RefFormId
;
2390 Selection
->QuestionId
= Statement
->RefQuestionId
;
2391 } else if (Statement
->RefFormId
!= 0) {
2393 // Goto another form inside this formset,
2395 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2398 // Link current form so that we can always go back when someone hits the UiPrevious
2400 UiAddMenuListEntry (Selection
);
2402 Selection
->FormId
= Statement
->RefFormId
;
2403 Selection
->QuestionId
= Statement
->RefQuestionId
;
2404 } else if (Statement
->RefQuestionId
!= 0) {
2406 // Goto another Question
2408 Selection
->QuestionId
= Statement
->RefQuestionId
;
2410 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2411 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2420 case EFI_IFR_ACTION_OP
:
2422 // Process the Config string <ConfigResp>
2424 Status
= ProcessQuestionConfig (Selection
, Statement
);
2426 if (EFI_ERROR (Status
)) {
2431 // The action button may change some Question value, so refresh the form
2433 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2436 case EFI_IFR_RESET_BUTTON_OP
:
2438 // Reset Question to default value specified by DefaultId
2440 ControlFlag
= CfUiDefault
;
2441 DefaultId
= Statement
->DefaultId
;
2446 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2448 UpdateKeyHelp (MenuOption
, TRUE
);
2449 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2451 if (EFI_ERROR (Status
)) {
2454 UpdateKeyHelp (MenuOption
, FALSE
);
2456 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2459 if (OptionString
!= NULL
) {
2460 FreePool (OptionString
);
2468 // We are going to leave current FormSet, so check uncommited data in this FormSet
2470 ControlFlag
= CfCheckSelection
;
2472 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
2474 // There is no parent menu for FrontPage
2476 Selection
->Action
= UI_ACTION_NONE
;
2477 Selection
->Statement
= MenuOption
->ThisTag
;
2482 // If NV flag is up, prompt user
2484 if (gNvUpdateRequired
) {
2485 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2487 YesResponse
= gYesResponse
[0];
2488 NoResponse
= gNoResponse
[0];
2491 CreateDialog (3, TRUE
, 0, NULL
, &Key
, gEmptyString
, gAreYouSure
, gEmptyString
);
2494 (Key
.ScanCode
!= SCAN_ESC
) &&
2495 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2496 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2500 // If the user hits the YesResponse key
2502 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2507 Selection
->Action
= UI_ACTION_NONE
;
2512 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2513 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2516 gST
->ConOut
->ClearScreen (gST
->ConOut
);
2520 ControlFlag
= CfCheckSelection
;
2521 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2522 if (MenuOption
->Sequence
!= 0) {
2524 // In the middle or tail of the Date/Time op-code set, go left.
2526 NewPos
= NewPos
->BackLink
;
2532 ControlFlag
= CfCheckSelection
;
2533 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2534 if (MenuOption
->Sequence
!= 2) {
2536 // In the middle or tail of the Date/Time op-code set, go left.
2538 NewPos
= NewPos
->ForwardLink
;
2544 ControlFlag
= CfCheckSelection
;
2546 SavedListEntry
= TopOfScreen
;
2548 if (NewPos
->BackLink
!= &Menu
) {
2551 // Adjust Date/Time position before we advance forward.
2553 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2556 // Caution that we have already rewind to the top, don't go backward in this situation.
2558 if (NewPos
->BackLink
!= &Menu
) {
2559 NewPos
= NewPos
->BackLink
;
2562 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2563 DistanceValue
= PreviousMenuOption
->Skip
;
2566 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2567 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2568 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2569 // checking can be done.
2571 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2574 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2575 // don't worry about a redraw.
2577 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2579 TopOfScreen
= NewPos
;
2582 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2583 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2584 if (Difference
> 0) {
2586 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2588 TopOfScreen
= NewPos
;
2592 if (Difference
< 0) {
2594 // We want to goto previous MenuOption, but finally we go down.
2595 // it means that we hit the begining MenuOption that can be focused
2596 // so we simply scroll to the top
2598 if (SavedListEntry
!= Menu
.ForwardLink
) {
2599 TopOfScreen
= Menu
.ForwardLink
;
2605 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2607 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2609 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2611 SavedMenuOption
= MenuOption
;
2612 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2613 if (!IsSelectable (MenuOption
)) {
2615 // If we are at the end of the list and sitting on a text op, we need to more forward
2617 ScreenOperation
= UiDown
;
2618 ControlFlag
= CfScreenOperation
;
2622 MenuOption
= SavedMenuOption
;
2627 ControlFlag
= CfCheckSelection
;
2629 if (NewPos
->BackLink
== &Menu
) {
2638 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2640 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2641 Index
= Index
- PreviousMenuOption
->Skip
;
2642 Link
= Link
->BackLink
;
2643 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2647 Difference
= MoveToNextStatement (TRUE
, &Link
);
2648 if (Difference
> 0) {
2650 // The focus MenuOption is above the TopOfScreen
2653 } else if (Difference
< 0) {
2655 // This happens when there is no MenuOption can be focused from
2656 // Current MenuOption to the first MenuOption
2658 TopOfScreen
= Menu
.ForwardLink
;
2660 Index
+= Difference
;
2661 if (Index
< TopRow
) {
2665 if (NewPos
== Link
) {
2673 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2674 // Don't do this when we are already in the first page.
2676 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2677 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2681 ControlFlag
= CfCheckSelection
;
2683 if (NewPos
->ForwardLink
== &Menu
) {
2692 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2694 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2695 Index
= Index
+ NextMenuOption
->Skip
;
2696 Link
= Link
->ForwardLink
;
2697 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2700 Index
+= MoveToNextStatement (FALSE
, &Link
);
2701 if (Index
> BottomRow
) {
2703 // There are more MenuOption needing scrolling
2708 if (NewPos
== Link
&& Index
<= BottomRow
) {
2710 // Finally we know that NewPos is the last MenuOption can be focused.
2719 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2720 // Don't do this when we are already in the last page.
2722 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2723 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2727 ControlFlag
= CfCheckSelection
;
2729 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2730 // to be one that progresses to the next set of op-codes, we need to advance to the last
2731 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2732 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2733 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2734 // the Date/Time op-code.
2736 SavedListEntry
= NewPos
;
2737 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2739 if (NewPos
->ForwardLink
!= &Menu
) {
2740 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2742 NewPos
= NewPos
->ForwardLink
;
2743 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2745 DistanceValue
+= NextMenuOption
->Skip
;
2746 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2748 // An option might be multi-line, so we need to reflect that data in the overall skip value
2750 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2752 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2753 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2754 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2755 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2761 // If we are going to scroll, update TopOfScreen
2763 if (Temp
> BottomRow
) {
2766 // Is the current top of screen a zero-advance op-code?
2767 // If so, keep moving forward till we hit a >0 advance op-code
2769 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2772 // If bottom op-code is more than one line or top op-code is more than one line
2774 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2776 // Is the bottom op-code greater than or equal in size to the top op-code?
2778 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2780 // Skip the top op-code
2782 TopOfScreen
= TopOfScreen
->ForwardLink
;
2783 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2785 OldSkipValue
= Difference
;
2787 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2790 // If we have a remainder, skip that many more op-codes until we drain the remainder
2793 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2794 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
2797 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2799 TopOfScreen
= TopOfScreen
->ForwardLink
;
2800 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2801 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
2802 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
2805 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
2806 TopOfScreen
= TopOfScreen
->ForwardLink
;
2807 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2808 Difference
= SavedMenuOption
->Skip
- Difference
;
2814 // Since we will act on this op-code in the next routine, and increment the
2815 // SkipValue, set the skips to one less than what is required.
2817 SkipValue
= Difference
- 1;
2821 // Since we will act on this op-code in the next routine, and increment the
2822 // SkipValue, set the skips to one less than what is required.
2824 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
2827 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
2828 TopOfScreen
= TopOfScreen
->ForwardLink
;
2831 SkipValue
= OldSkipValue
;
2835 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2836 // Let's set a skip flag to smoothly scroll the top of the screen.
2838 if (SavedMenuOption
->Skip
> 1) {
2839 if (SavedMenuOption
== NextMenuOption
) {
2846 TopOfScreen
= TopOfScreen
->ForwardLink
;
2848 } while (SavedMenuOption
->Skip
== 0);
2851 OldSkipValue
= SkipValue
;
2854 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
2856 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2859 SavedMenuOption
= MenuOption
;
2860 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2861 if (!IsSelectable (MenuOption
)) {
2863 // If we are at the end of the list and sitting on a text op, we need to more forward
2865 ScreenOperation
= UiUp
;
2866 ControlFlag
= CfScreenOperation
;
2870 MenuOption
= SavedMenuOption
;
2872 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2874 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2879 ControlFlag
= CfCheckSelection
;
2884 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2886 if (!EFI_ERROR (Status
)) {
2887 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2888 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2891 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
2892 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2900 ControlFlag
= CfCheckSelection
;
2902 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
2904 if (!EFI_ERROR (Status
)) {
2905 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2906 Selection
->Statement
= NULL
;
2909 // Show NV update flag on status bar
2911 gNvUpdateRequired
= TRUE
;
2915 case CfUiNoOperation
:
2916 ControlFlag
= CfCheckSelection
;
2920 UiFreeRefreshList ();
2922 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2923 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
2924 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2925 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");