2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2007, 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
[] = {
121 Set Buffer to Value for Size bytes.
123 @param Buffer Memory to set.
124 @param Size Number of bytes to set
125 @param Value Value of the set operation.
138 while ((Size
--) != 0) {
145 Initialize Menu option list.
153 InitializeListHead (&Menu
);
158 Initialize Menu option list.
166 InitializeListHead (&gMenuList
);
171 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
173 @param Selection Menu selection.
177 UiRemoveMenuListEntry (
178 OUT UI_MENU_SELECTION
*Selection
181 UI_MENU_LIST
*UiMenuList
;
183 if (!IsListEmpty (&gMenuList
)) {
184 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
186 Selection
->FormId
= UiMenuList
->FormId
;
187 Selection
->QuestionId
= UiMenuList
->QuestionId
;
188 RemoveEntryList (&UiMenuList
->MenuLink
);
189 gBS
->FreePool (UiMenuList
);
195 Free Menu option linked list.
203 UI_MENU_LIST
*UiMenuList
;
205 while (!IsListEmpty (&gMenuList
)) {
206 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
207 RemoveEntryList (&UiMenuList
->MenuLink
);
208 gBS
->FreePool (UiMenuList
);
214 Add one menu entry to the linked lst
216 @param Selection Menu selection.
221 IN UI_MENU_SELECTION
*Selection
224 UI_MENU_LIST
*UiMenuList
;
226 UiMenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
227 ASSERT (UiMenuList
!= NULL
);
229 UiMenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
230 UiMenuList
->FormId
= Selection
->FormId
;
231 UiMenuList
->QuestionId
= Selection
->QuestionId
;
233 InsertHeadList (&gMenuList
, &UiMenuList
->MenuLink
);
238 Free Menu option linked list.
246 UI_MENU_OPTION
*MenuOption
;
248 while (!IsListEmpty (&Menu
)) {
249 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
250 RemoveEntryList (&MenuOption
->Link
);
253 // We allocated space for this description when we did a GetToken, free it here
255 if (MenuOption
->Skip
!= 0) {
257 // For date/time, MenuOption->Description is shared by three Menu Options
258 // Data format : [01/02/2004] [11:22:33]
259 // Line number : 0 0 1 0 0 1
261 gBS
->FreePool (MenuOption
->Description
);
263 gBS
->FreePool (MenuOption
);
269 Free Menu option linked list.
277 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
279 while (gMenuRefreshHead
!= NULL
) {
280 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
281 gBS
->FreePool (gMenuRefreshHead
);
282 gMenuRefreshHead
= OldMenuRefreshEntry
;
285 gMenuRefreshHead
= NULL
;
299 CHAR16
*OptionString
;
300 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
304 UI_MENU_SELECTION
*Selection
;
305 FORM_BROWSER_STATEMENT
*Question
;
309 if (gMenuRefreshHead
!= NULL
) {
311 MenuRefreshEntry
= gMenuRefreshHead
;
314 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
316 Selection
= MenuRefreshEntry
->Selection
;
317 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
320 // Don't update Question being edited
322 if (Question
!= MenuRefreshEntry
->Selection
->Statement
) {
324 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
325 if (EFI_ERROR (Status
)) {
329 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
331 if (OptionString
!= NULL
) {
333 // If leading spaces on OptionString - remove the spaces
335 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
338 for (Loop
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
339 OptionString
[Loop
] = OptionString
[Index
];
343 OptionString
[Loop
] = CHAR_NULL
;
345 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
346 gBS
->FreePool (OptionString
);
350 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
352 } while (MenuRefreshEntry
!= NULL
);
358 Wait for a given event to fire, or for an optional timeout to expire.
360 @param Event The event to wait for
361 @param Timeout An optional timeout value in 100 ns units.
362 @param RefreshInterval Menu refresh interval (in seconds).
364 @retval EFI_SUCCESS Event fired before Timeout expired.
365 @retval EFI_TIME_OUT Timout expired before Event fired.
369 UiWaitForSingleEvent (
371 IN UINT64 Timeout
, OPTIONAL
372 IN UINT8 RefreshInterval OPTIONAL
377 EFI_EVENT TimerEvent
;
378 EFI_EVENT WaitList
[2];
382 // Create a timer event
384 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
385 if (!EFI_ERROR (Status
)) {
387 // Set the timer event
396 // Wait for the original event or the timer
399 WaitList
[1] = TimerEvent
;
400 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
401 gBS
->CloseEvent (TimerEvent
);
404 // If the timer expired, change the return to timed out
406 if (!EFI_ERROR (Status
) && Index
== 1) {
407 Status
= EFI_TIMEOUT
;
412 // Update screen every second
414 if (RefreshInterval
== 0) {
415 Timeout
= ONE_SECOND
;
417 Timeout
= RefreshInterval
* ONE_SECOND
;
421 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
424 // Set the timer event
433 // Wait for the original event or the timer
436 WaitList
[1] = TimerEvent
;
437 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
440 // If the timer expired, update anything that needs a refresh and keep waiting
442 if (!EFI_ERROR (Status
) && Index
== 1) {
443 Status
= EFI_TIMEOUT
;
444 if (RefreshInterval
!= 0) {
449 gBS
->CloseEvent (TimerEvent
);
450 } while (Status
== EFI_TIMEOUT
);
458 Add one menu option by specified description and context.
460 @param String String description for this option.
461 @param Handle Hii handle for the package list.
462 @param Statement Statement of this Menu Option.
463 @param NumberOfLines Display lines for this Menu Option.
464 @param MenuItemCount The index for this Option in the Menu.
470 IN EFI_HII_HANDLE Handle
,
471 IN FORM_BROWSER_STATEMENT
*Statement
,
472 IN UINT16 NumberOfLines
,
473 IN UINT16 MenuItemCount
476 UI_MENU_OPTION
*MenuOption
;
482 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
484 // Add three MenuOptions for Date/Time
485 // Data format : [01/02/2004] [11:22:33]
486 // Line number : 0 0 1 0 0 1
491 if (Statement
->Storage
== NULL
) {
493 // For RTC type of date/time, set default refresh interval to be 1 second
495 if (Statement
->RefreshInterval
== 0) {
496 Statement
->RefreshInterval
= 1;
501 for (Index
= 0; Index
< Count
; Index
++) {
502 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
505 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
506 MenuOption
->Description
= String
;
507 MenuOption
->Handle
= Handle
;
508 MenuOption
->ThisTag
= Statement
;
509 MenuOption
->EntryNumber
= MenuItemCount
;
513 // Override LineNumber for the MenuOption in Date/Time sequence
515 MenuOption
->Skip
= 1;
517 MenuOption
->Skip
= NumberOfLines
;
519 MenuOption
->Sequence
= Index
;
521 if (Statement
->GrayOutExpression
!= NULL
) {
522 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
525 if ((Statement
->ValueExpression
!= NULL
) ||
526 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
527 MenuOption
->ReadOnly
= TRUE
;
530 InsertTailList (&Menu
, &MenuOption
->Link
);
536 Routine used to abstract a generic dialog interface and return the selected key or string
538 @param NumberOfLines The number of lines for the dialog box
539 @param HotKey Defines whether a single character is parsed
540 (TRUE) and returned in KeyValue or a string is
541 returned in StringBuffer. Two special characters
542 are considered when entering a string, a SCAN_ESC
543 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
544 string input and returns
545 @param MaximumStringSize The maximum size in bytes of a typed in string
546 (each character is a CHAR16) and the minimum
547 string returned is two bytes
548 @param StringBuffer The passed in pointer to the buffer which will
549 hold the typed in string if HotKey is FALSE
550 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
551 @param String Pointer to the first string in the list
552 @param ... A series of (quantity == NumberOfLines) text
553 strings which will be used to construct the dialog
556 @retval EFI_SUCCESS Displayed dialog and received user interaction
557 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
558 (StringBuffer == NULL) && (HotKey == FALSE))
559 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
564 IN UINTN NumberOfLines
,
566 IN UINTN MaximumStringSize
,
567 OUT CHAR16
*StringBuffer
,
568 OUT EFI_INPUT_KEY
*KeyValue
,
578 CHAR16
*BufferedString
;
585 BOOLEAN SelectionComplete
;
587 UINTN CurrentAttribute
;
588 UINTN DimensionsWidth
;
589 UINTN DimensionsHeight
;
591 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
592 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
594 SelectionComplete
= FALSE
;
596 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
597 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
598 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
601 ASSERT (BufferedString
);
603 VA_START (Marker
, String
);
606 // Zero the outgoing buffer
608 ZeroMem (StringBuffer
, MaximumStringSize
);
611 if (KeyValue
== NULL
) {
612 return EFI_INVALID_PARAMETER
;
615 if (StringBuffer
== NULL
) {
616 return EFI_INVALID_PARAMETER
;
622 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
624 LargestString
= (GetStringWidth (String
) / 2);
626 if (*String
== L
' ') {
630 // Determine the largest string in the dialog box
631 // Notice we are starting with 1 since String is the first string
633 for (Count
= 1; Count
< NumberOfLines
; Count
++) {
634 StackString
= VA_ARG (Marker
, CHAR16
*);
636 if (StackString
[0] == L
' ') {
637 InputOffset
= Count
+ 1;
640 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
642 // Size of the string visually and subtract the width by one for the null-terminator
644 LargestString
= (GetStringWidth (StackString
) / 2);
648 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
649 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
656 CreateSharedPopUp (LargestString
, NumberOfLines
, &String
);
659 // Take the first key typed and report it back?
662 Status
= WaitForKeyStroke (&Key
);
663 ASSERT_EFI_ERROR (Status
);
664 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
668 Status
= WaitForKeyStroke (&Key
);
670 switch (Key
.UnicodeChar
) {
672 switch (Key
.ScanCode
) {
674 gBS
->FreePool (TempString
);
675 gBS
->FreePool (BufferedString
);
676 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
677 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
678 return EFI_DEVICE_ERROR
;
686 case CHAR_CARRIAGE_RETURN
:
687 SelectionComplete
= TRUE
;
688 gBS
->FreePool (TempString
);
689 gBS
->FreePool (BufferedString
);
690 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
691 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
696 if (StringBuffer
[0] != CHAR_NULL
) {
697 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
698 TempString
[Index
] = StringBuffer
[Index
];
701 // Effectively truncate string by 1 character
703 TempString
[Index
- 1] = CHAR_NULL
;
704 StrCpy (StringBuffer
, TempString
);
709 // If it is the beginning of the string, don't worry about checking maximum limits
711 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
712 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
713 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
714 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
715 KeyPad
[0] = Key
.UnicodeChar
;
716 KeyPad
[1] = CHAR_NULL
;
717 StrCat (StringBuffer
, KeyPad
);
718 StrCat (TempString
, KeyPad
);
721 // If the width of the input string is now larger than the screen, we nee to
722 // adjust the index to start printing portions of the string
724 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
726 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
728 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
729 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
734 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
735 BufferedString
[Count
] = StringBuffer
[Index
];
738 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
741 } while (!SelectionComplete
);
744 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
745 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
750 Draw a pop up windows based on the dimension, number of lines and
753 @param RequestedWidth The width of the pop-up.
754 @param NumberOfLines The number of lines.
755 @param ArrayOfStrings The array of string to be printed.
760 IN UINTN RequestedWidth
,
761 IN UINTN NumberOfLines
,
762 IN CHAR16
**ArrayOfStrings
773 UINTN DimensionsWidth
;
774 UINTN DimensionsHeight
;
776 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
777 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
781 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
783 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
784 RequestedWidth
= DimensionsWidth
- 2;
788 // Subtract the PopUp width from total Columns, allow for one space extra on
789 // each end plus a border.
791 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
792 End
= Start
+ RequestedWidth
+ 1;
794 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
795 Bottom
= Top
+ NumberOfLines
+ 2;
797 Character
= BOXDRAW_DOWN_RIGHT
;
798 PrintCharAt (Start
, Top
, Character
);
799 Character
= BOXDRAW_HORIZONTAL
;
800 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
801 PrintChar (Character
);
804 Character
= BOXDRAW_DOWN_LEFT
;
805 PrintChar (Character
);
806 Character
= BOXDRAW_VERTICAL
;
807 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++) {
808 String
= ArrayOfStrings
[Count
];
812 // This will clear the background of the line - we never know who might have been
813 // here before us. This differs from the next clear in that it used the non-reverse
814 // video for normal printing.
816 if (GetStringWidth (String
) / 2 > 1) {
817 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
821 // Passing in a space results in the assumption that this is where typing will occur
823 if (String
[0] == L
' ') {
824 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
828 // Passing in a NULL results in a blank space
830 if (String
[0] == CHAR_NULL
) {
831 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
835 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
839 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
840 PrintCharAt (Start
, Index
+ 1, Character
);
841 PrintCharAt (End
- 1, Index
+ 1, Character
);
844 Character
= BOXDRAW_UP_RIGHT
;
845 PrintCharAt (Start
, Bottom
- 1, Character
);
846 Character
= BOXDRAW_HORIZONTAL
;
847 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
848 PrintChar (Character
);
851 Character
= BOXDRAW_UP_LEFT
;
852 PrintChar (Character
);
856 Draw a pop up windows based on the dimension, number of lines and
859 @param RequestedWidth The width of the pop-up.
860 @param NumberOfLines The number of lines.
861 @param ArrayOfStrings The array of string to be printed.
862 @param ... A series of text strings that displayed in the pop-up.
867 IN UINTN RequestedWidth
,
868 IN UINTN NumberOfLines
,
869 IN CHAR16
*ArrayOfStrings
,
873 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, &ArrayOfStrings
);
878 Update status bar on the bottom of menu.
880 @param MessageType The type of message to be shown.
881 @param Flags The flags in Question header.
882 @param State Set or clear.
887 IN UINTN MessageType
,
893 STATIC BOOLEAN InputError
;
894 CHAR16
*NvUpdateMessage
;
895 CHAR16
*InputErrorMessage
;
897 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
898 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
900 switch (MessageType
) {
903 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
905 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
906 gScreenDimensions
.BottomRow
- 1,
911 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
912 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
913 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
920 case NV_UPDATE_REQUIRED
:
921 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
923 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
925 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
926 gScreenDimensions
.BottomRow
- 1,
929 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
931 gNvUpdateRequired
= TRUE
;
933 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
934 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
936 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
937 gScreenDimensions
.BottomRow
- 1,
942 gNvUpdateRequired
= FALSE
;
947 case REFRESH_STATUS_BAR
:
949 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
952 if (gNvUpdateRequired
) {
953 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
961 gBS
->FreePool (InputErrorMessage
);
962 gBS
->FreePool (NvUpdateMessage
);
968 Get the supported width for a particular op-code
970 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
971 @param Handle The handle in the HII database being used
973 @return Returns the number of CHAR16 characters that is support.
978 IN FORM_BROWSER_STATEMENT
*Statement
,
979 IN EFI_HII_HANDLE Handle
989 // See if the second text parameter is really NULL
991 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
992 String
= GetToken (Statement
->TextTwo
, Handle
);
993 Size
= StrLen (String
);
994 gBS
->FreePool (String
);
997 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
998 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
999 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1000 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1001 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1003 // Allow a wide display if text op-code and no secondary text op-code
1005 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1007 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1009 Width
= (UINT16
) gPromptBlockWidth
;
1012 if (Statement
->InSubtitle
) {
1013 Width
-= SUBTITLE_INDENT
;
1020 STATIC BOOLEAN GetLineByWidthFinished
= FALSE
;
1023 Will copy LineWidth amount of a string in the OutputString buffer and return the
1024 number of CHAR16 characters that were copied into the OutputString buffer.
1026 @param InputString String description for this option.
1027 @param LineWidth Width of the desired string to extract in CHAR16
1029 @param Index Where in InputString to start the copy process
1030 @param OutputString Buffer to copy the string into
1032 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1037 IN CHAR16
*InputString
,
1038 IN UINT16 LineWidth
,
1039 IN OUT UINTN
*Index
,
1040 OUT CHAR16
**OutputString
1046 if (GetLineByWidthFinished
) {
1047 GetLineByWidthFinished
= FALSE
;
1054 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1057 // Ensure we have got a valid buffer
1059 if (*OutputString
!= NULL
) {
1062 //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.
1063 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1065 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1066 *Index
= *Index
+ 2;
1070 // Fast-forward the string and see if there is a carriage-return in the string
1072 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1076 // Copy the desired LineWidth of data to the output buffer.
1077 // Also make sure that we don't copy more than the string.
1078 // Also make sure that if there are linefeeds, we account for them.
1080 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1081 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1084 // Convert to CHAR16 value and show that we are done with this operation
1086 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1087 if (LineWidth
!= 0) {
1088 GetLineByWidthFinished
= TRUE
;
1091 if (Count2
== LineWidth
) {
1093 // Rewind the string from the maximum size until we see a space to break the line
1095 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1097 if (LineWidth
== 0) {
1105 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1108 // If currently pointing to a space, increment the index to the first non-space character
1111 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1115 *Index
= (UINT16
) (*Index
+ LineWidth
);
1124 Update display lines for a Menu Option.
1126 @param Selection The user's selection.
1127 @param MenuOption The MenuOption to be checked.
1128 @param OptionalString The option string.
1129 @param SkipValue The number of lins to skip.
1131 @retval TRUE This Menu Option is selectable.
1132 @retval FALSE This Menu Option could not be selected.
1136 UpdateOptionSkipLines (
1137 IN UI_MENU_SELECTION
*Selection
,
1138 IN UI_MENU_OPTION
*MenuOption
,
1139 OUT CHAR16
**OptionalString
,
1147 CHAR16
*OutputString
;
1148 CHAR16
*OptionString
;
1151 OptionString
= *OptionalString
;
1152 OutputString
= NULL
;
1154 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1156 if (OptionString
!= NULL
) {
1157 Width
= (UINT16
) gOptionBlockWidth
;
1161 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1163 // If there is more string to process print on the next row and increment the Skip value
1165 if (StrLen (&OptionString
[Index
])) {
1166 if (SkipValue
== 0) {
1169 // Since the Number of lines for this menu entry may or may not be reflected accurately
1170 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1171 // some testing to ensure we are keeping this in-sync.
1173 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1175 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1181 gBS
->FreePool (OutputString
);
1182 if (SkipValue
!= 0) {
1190 *OptionalString
= OptionString
;
1195 Check whether this Menu Option could be highlighted.
1197 This is an internal function.
1199 @param MenuOption The MenuOption to be checked.
1201 @retval TRUE This Menu Option is selectable.
1202 @retval FALSE This Menu Option could not be selected.
1207 UI_MENU_OPTION
*MenuOption
1210 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1211 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1220 Determine if the menu is the last menu that can be selected.
1222 This is an internal function.
1224 @param Direction The scroll direction. False is down. True is up.
1225 @param CurrentPos The current focus.
1227 @return FALSE -- the menu isn't the last menu that can be selected.
1228 @return TRUE -- the menu is the last menu that can be selected.
1233 IN BOOLEAN Direction
,
1234 IN LIST_ENTRY
*CurrentPos
1238 UI_MENU_OPTION
*MenuOption
;
1240 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1242 if (Temp
== &Menu
) {
1246 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1247 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1248 if (IsSelectable (MenuOption
)) {
1258 Move to next selectable statement.
1260 This is an internal function.
1262 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1263 @param CurrentPosition Current position.
1265 @return The row distance from current MenuOption to next selectable MenuOption.
1269 MoveToNextStatement (
1271 IN OUT LIST_ENTRY
**CurrentPosition
1277 UI_MENU_OPTION
*NextMenuOption
;
1280 Pos
= *CurrentPosition
;
1284 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1285 if (IsSelectable (NextMenuOption
)) {
1288 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1292 Distance
+= NextMenuOption
->Skip
;
1293 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1298 // If we hit end there is still no statement can be focused,
1299 // we go backwards to find the statement can be focused.
1302 Pos
= *CurrentPosition
;
1305 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1306 if (IsSelectable (NextMenuOption
)) {
1309 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1313 Distance
-= NextMenuOption
->Skip
;
1314 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1318 *CurrentPosition
= &NextMenuOption
->Link
;
1324 Adjust Data and Time position accordingly.
1325 Data format : [01/02/2004] [11:22:33]
1326 Line number : 0 0 1 0 0 1
1328 This is an internal function.
1330 @param DirectionUp the up or down direction. False is down. True is
1332 @param CurrentPosition Current position. On return: Point to the last
1333 Option (Year or Second) if up; Point to the first
1334 Option (Month or Hour) if down.
1336 @return Return line number to pad. It is possible that we stand on a zero-advance
1337 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1341 AdjustDateAndTimePosition (
1342 IN BOOLEAN DirectionUp
,
1343 IN OUT LIST_ENTRY
**CurrentPosition
1347 LIST_ENTRY
*NewPosition
;
1348 UI_MENU_OPTION
*MenuOption
;
1349 UINTN PadLineNumber
;
1352 NewPosition
= *CurrentPosition
;
1353 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1355 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1356 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1358 // Calculate the distance from current position to the last Date/Time MenuOption
1361 while (MenuOption
->Skip
== 0) {
1363 NewPosition
= NewPosition
->ForwardLink
;
1364 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1368 NewPosition
= *CurrentPosition
;
1371 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1372 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1373 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1374 // checking can be done.
1376 while (Count
++ < 2) {
1377 NewPosition
= NewPosition
->BackLink
;
1381 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1382 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1383 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1384 // checking can be done.
1386 while (Count
-- > 0) {
1387 NewPosition
= NewPosition
->ForwardLink
;
1391 *CurrentPosition
= NewPosition
;
1394 return PadLineNumber
;
1399 Display menu and wait for user to select one menu option, then return it.
1400 If AutoBoot is enabled, then if user doesn't select any option,
1401 after period of time, it will automatically return the first menu option.
1403 @param Selection Menu selection.
1405 @retval EFI_SUCESSS This function always return successfully for now.
1410 IN OUT UI_MENU_SELECTION
*Selection
1416 UINTN DistanceValue
;
1428 CHAR16
*OptionString
;
1429 CHAR16
*OutputString
;
1430 CHAR16
*FormattedString
;
1440 LIST_ENTRY
*TopOfScreen
;
1441 LIST_ENTRY
*SavedListEntry
;
1442 UI_MENU_OPTION
*MenuOption
;
1443 UI_MENU_OPTION
*NextMenuOption
;
1444 UI_MENU_OPTION
*SavedMenuOption
;
1445 UI_MENU_OPTION
*PreviousMenuOption
;
1446 UI_CONTROL_FLAG ControlFlag
;
1447 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1448 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1449 UI_SCREEN_OPERATION ScreenOperation
;
1450 UINT8 MinRefreshInterval
;
1453 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1454 FORM_BROWSER_STATEMENT
*Statement
;
1456 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1458 Status
= EFI_SUCCESS
;
1459 FormattedString
= NULL
;
1460 OptionString
= NULL
;
1461 ScreenOperation
= UiNoOperation
;
1463 MinRefreshInterval
= 0;
1466 OutputString
= NULL
;
1471 MenuRefreshEntry
= gMenuRefreshHead
;
1473 NextMenuOption
= NULL
;
1474 PreviousMenuOption
= NULL
;
1475 SavedMenuOption
= NULL
;
1477 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1479 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
1480 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1481 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1483 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1484 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1487 Col
= LocalScreen
.LeftColumn
;
1488 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1490 Selection
->TopRow
= TopRow
;
1491 Selection
->BottomRow
= BottomRow
;
1492 Selection
->PromptCol
= Col
;
1493 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1494 Selection
->Statement
= NULL
;
1496 TopOfScreen
= Menu
.ForwardLink
;
1501 // Get user's selection
1503 NewPos
= Menu
.ForwardLink
;
1505 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1506 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1508 ControlFlag
= CfInitialization
;
1509 Selection
->Action
= UI_ACTION_NONE
;
1511 switch (ControlFlag
) {
1512 case CfInitialization
:
1513 if (IsListEmpty (&Menu
)) {
1514 ControlFlag
= CfReadKey
;
1516 ControlFlag
= CfCheckSelection
;
1520 case CfCheckSelection
:
1521 if (Selection
->Action
!= UI_ACTION_NONE
) {
1522 ControlFlag
= CfExit
;
1524 ControlFlag
= CfRepaint
;
1529 ControlFlag
= CfRefreshHighLight
;
1543 LocalScreen
.LeftColumn
,
1544 LocalScreen
.RightColumn
,
1545 TopRow
- SCROLL_ARROW_HEIGHT
,
1546 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1547 FIELD_TEXT
| FIELD_BACKGROUND
1550 UiFreeRefreshList ();
1551 MinRefreshInterval
= 0;
1553 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1554 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1555 MenuOption
->Row
= Row
;
1556 MenuOption
->Col
= Col
;
1557 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1559 Statement
= MenuOption
->ThisTag
;
1560 if (Statement
->InSubtitle
) {
1561 MenuOption
->Col
+= SUBTITLE_INDENT
;
1564 if (MenuOption
->GrayOut
) {
1565 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1567 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1568 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1572 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1575 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1576 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1577 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1580 // If there is more string to process print on the next row and increment the Skip value
1582 if (StrLen (&MenuOption
->Description
[Index
])) {
1588 gBS
->FreePool (OutputString
);
1597 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1598 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1600 if (OptionString
!= NULL
) {
1601 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1603 // If leading spaces on OptionString - remove the spaces
1605 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1606 MenuOption
->OptCol
++;
1609 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1610 OptionString
[Count
] = OptionString
[Index
];
1614 OptionString
[Count
] = CHAR_NULL
;
1618 // If Question request refresh, register the op-code
1620 if (Statement
->RefreshInterval
!= 0) {
1622 // Menu will be refreshed at minimal interval of all Questions
1623 // which have refresh request
1625 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1626 MinRefreshInterval
= Statement
->RefreshInterval
;
1629 if (gMenuRefreshHead
== NULL
) {
1630 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1631 ASSERT (MenuRefreshEntry
!= NULL
);
1632 MenuRefreshEntry
->MenuOption
= MenuOption
;
1633 MenuRefreshEntry
->Selection
= Selection
;
1634 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1635 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1636 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1637 gMenuRefreshHead
= MenuRefreshEntry
;
1640 // Advance to the last entry
1642 for (MenuRefreshEntry
= gMenuRefreshHead
;
1643 MenuRefreshEntry
->Next
!= NULL
;
1644 MenuRefreshEntry
= MenuRefreshEntry
->Next
1647 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1648 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1649 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1650 MenuRefreshEntry
->MenuOption
= MenuOption
;
1651 MenuRefreshEntry
->Selection
= Selection
;
1652 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1653 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1654 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1658 Width
= (UINT16
) gOptionBlockWidth
;
1661 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1662 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1663 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1666 // If there is more string to process print on the next row and increment the Skip value
1668 if (StrLen (&OptionString
[Index
])) {
1672 // Since the Number of lines for this menu entry may or may not be reflected accurately
1673 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1674 // some testing to ensure we are keeping this in-sync.
1676 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1678 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1684 gBS
->FreePool (OutputString
);
1693 gBS
->FreePool (OptionString
);
1696 // If this is a text op with secondary text information
1698 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1699 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1701 Width
= (UINT16
) gOptionBlockWidth
;
1704 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1705 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1706 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1709 // If there is more string to process print on the next row and increment the Skip value
1711 if (StrLen (&StringPtr
[Index
])) {
1715 // Since the Number of lines for this menu entry may or may not be reflected accurately
1716 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1717 // some testing to ensure we are keeping this in-sync.
1719 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1721 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1727 gBS
->FreePool (OutputString
);
1734 gBS
->FreePool (StringPtr
);
1738 // Need to handle the bottom of the display
1740 if (MenuOption
->Skip
> 1) {
1741 Row
+= MenuOption
->Skip
- SkipValue
;
1744 Row
+= MenuOption
->Skip
;
1747 if (Row
> BottomRow
) {
1748 if (!ValueIsScroll (FALSE
, Link
)) {
1752 Row
= BottomRow
+ 1;
1757 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
1762 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1764 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1765 TopRow
- SCROLL_ARROW_HEIGHT
,
1769 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1773 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1775 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1776 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1780 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1787 case CfRefreshHighLight
:
1789 // MenuOption: Last menu option that need to remove hilight
1790 // MenuOption is set to NULL in Repaint
1791 // NewPos: Current menu option that need to hilight
1793 ControlFlag
= CfUpdateHelpString
;
1796 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1797 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1799 SavedValue
= Repaint
;
1802 if (Selection
->QuestionId
!= 0) {
1803 NewPos
= Menu
.ForwardLink
;
1804 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1806 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
1807 NewPos
= NewPos
->ForwardLink
;
1808 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1810 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
1812 // Target Question found, find its MenuOption
1816 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
1817 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1818 Index
+= SavedMenuOption
->Skip
;
1819 Link
= Link
->ForwardLink
;
1822 if (Link
!= NewPos
|| Index
> BottomRow
) {
1824 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1827 for (Index
= TopRow
; Index
<= BottomRow
; ) {
1828 Link
= Link
->BackLink
;
1829 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1830 Index
+= SavedMenuOption
->Skip
;
1832 TopOfScreen
= Link
->ForwardLink
;
1836 ControlFlag
= CfRepaint
;
1841 // Target Question not found, highlight the default menu option
1843 NewPos
= TopOfScreen
;
1846 Selection
->QuestionId
= 0;
1849 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
1850 if (MenuOption
!= NULL
) {
1852 // Remove highlight on last Menu Option
1854 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1855 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1856 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1857 if (OptionString
!= NULL
) {
1858 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1859 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
1862 // If leading spaces on OptionString - remove the spaces
1864 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1867 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1868 OptionString
[Count
] = OptionString
[Index
];
1872 OptionString
[Count
] = CHAR_NULL
;
1875 Width
= (UINT16
) gOptionBlockWidth
;
1876 OriginalRow
= MenuOption
->Row
;
1878 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1879 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1880 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1883 // If there is more string to process print on the next row and increment the Skip value
1885 if (StrLen (&OptionString
[Index
])) {
1889 gBS
->FreePool (OutputString
);
1892 MenuOption
->Row
= OriginalRow
;
1894 gBS
->FreePool (OptionString
);
1897 if (MenuOption
->GrayOut
) {
1898 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1899 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1900 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1903 OriginalRow
= MenuOption
->Row
;
1904 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
1906 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1907 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1908 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1911 // If there is more string to process print on the next row and increment the Skip value
1913 if (StrLen (&MenuOption
->Description
[Index
])) {
1917 gBS
->FreePool (OutputString
);
1920 MenuOption
->Row
= OriginalRow
;
1921 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1927 // This is only possible if we entered this page and the first menu option is
1928 // a "non-menu" item. In that case, force it UiDown
1930 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1931 if (!IsSelectable (MenuOption
)) {
1932 ASSERT (ScreenOperation
== UiNoOperation
);
1933 ScreenOperation
= UiDown
;
1934 ControlFlag
= CfScreenOperation
;
1939 // This is the current selected statement
1941 Statement
= MenuOption
->ThisTag
;
1942 Selection
->Statement
= Statement
;
1945 // Set reverse attribute
1947 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
1948 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1951 // Assuming that we have a refresh linked-list created, lets annotate the
1952 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1953 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1955 if (gMenuRefreshHead
!= NULL
) {
1956 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
1957 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1958 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
1959 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
1964 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1965 if (OptionString
!= NULL
) {
1966 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1968 // If leading spaces on OptionString - remove the spaces
1970 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1973 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1974 OptionString
[Count
] = OptionString
[Index
];
1978 OptionString
[Count
] = CHAR_NULL
;
1980 Width
= (UINT16
) gOptionBlockWidth
;
1982 OriginalRow
= MenuOption
->Row
;
1984 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1985 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1986 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1989 // If there is more string to process print on the next row and increment the Skip value
1991 if (StrLen (&OptionString
[Index
])) {
1995 gBS
->FreePool (OutputString
);
1998 MenuOption
->Row
= OriginalRow
;
2000 gBS
->FreePool (OptionString
);
2003 OriginalRow
= MenuOption
->Row
;
2005 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2007 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2008 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2009 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2012 // If there is more string to process print on the next row and increment the Skip value
2014 if (StrLen (&MenuOption
->Description
[Index
])) {
2018 gBS
->FreePool (OutputString
);
2021 MenuOption
->Row
= OriginalRow
;
2026 if (((NewPos
->ForwardLink
!= &Menu
) && (ScreenOperation
== UiDown
)) ||
2027 ((NewPos
->BackLink
!= &Menu
) && (ScreenOperation
== UiUp
)) ||
2028 (ScreenOperation
== UiNoOperation
)
2030 UpdateKeyHelp (MenuOption
, FALSE
);
2033 // Clear reverse attribute
2035 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2038 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2039 // if we didn't break halfway when process CfRefreshHighLight.
2041 Repaint
= SavedValue
;
2044 case CfUpdateHelpString
:
2045 ControlFlag
= CfPrepareToReadKey
;
2047 if ((Repaint
|| NewLine
) && (gClassOfVfr
!= EFI_GENERAL_APPLICATION_SUBCLASS
)) {
2049 // Don't print anything if it is a NULL help token
2051 if (MenuOption
->ThisTag
->Help
== 0) {
2054 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2057 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2059 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2061 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2063 // Pad String with spaces to simulate a clearing of the previous line
2065 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2066 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2070 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2072 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2077 // Reset this flag every time we finish using it.
2083 case CfPrepareToReadKey
:
2084 ControlFlag
= CfReadKey
;
2085 ScreenOperation
= UiNoOperation
;
2089 ControlFlag
= CfScreenOperation
;
2092 // Wait for user's selection
2095 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2096 } while (Status
== EFI_TIMEOUT
);
2098 if (Status
== EFI_TIMEOUT
) {
2099 Key
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
2101 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2103 // if we encounter error, continue to read another key in.
2105 if (EFI_ERROR (Status
)) {
2106 ControlFlag
= CfReadKey
;
2111 if (IsListEmpty (&Menu
) && Key
.UnicodeChar
!= CHAR_NULL
) {
2113 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2118 switch (Key
.UnicodeChar
) {
2119 case CHAR_CARRIAGE_RETURN
:
2120 ScreenOperation
= UiSelect
;
2125 // We will push the adjustment of these numeric values directly to the input handler
2126 // NOTE: we won't handle manual input numeric
2130 Statement
= MenuOption
->ThisTag
;
2131 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2132 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2133 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2135 if (Key
.UnicodeChar
== '+') {
2136 gDirection
= SCAN_RIGHT
;
2138 gDirection
= SCAN_LEFT
;
2140 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2141 SafeFreePool (OptionString
);
2146 ScreenOperation
= UiUp
;
2151 ScreenOperation
= UiDown
;
2155 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
2156 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2157 ScreenOperation
= UiSelect
;
2163 if (((Key
.ScanCode
== SCAN_F1
) && ((gFunctionKeySetting
& FUNCTION_ONE
) != FUNCTION_ONE
)) ||
2164 ((Key
.ScanCode
== SCAN_F2
) && ((gFunctionKeySetting
& FUNCTION_TWO
) != FUNCTION_TWO
)) ||
2165 ((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2166 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2169 // If the function key has been disabled, just ignore the key.
2172 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2173 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2174 if (Key
.ScanCode
== SCAN_F9
) {
2176 // Reset to standard default
2178 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2180 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2189 case CfScreenOperation
:
2190 if (ScreenOperation
!= UiPrevious
&& ScreenOperation
!= UiReset
) {
2192 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2193 // ignore the selection and go back to reading keys.
2195 if (IsListEmpty (&Menu
)) {
2196 ControlFlag
= CfReadKey
;
2200 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2202 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2203 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2204 if (IsSelectable (NextMenuOption
)) {
2209 if (Link
== &Menu
) {
2210 ControlFlag
= CfPrepareToReadKey
;
2213 } else if (ScreenOperation
== UiReset
) {
2215 // Press ESC to exit FormSet
2217 Selection
->Action
= UI_ACTION_EXIT
;
2218 Selection
->Statement
= NULL
;
2222 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2225 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2226 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2233 ControlFlag
= CfCheckSelection
;
2235 if (IsListEmpty (&gMenuList
)) {
2236 Selection
->Action
= UI_ACTION_NONE
;
2237 if (IsListEmpty (&Menu
)) {
2238 ControlFlag
= CfReadKey
;
2244 // Remove the Cached page entry
2246 UiRemoveMenuListEntry (Selection
);
2248 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2249 Selection
->Statement
= NULL
;
2253 ControlFlag
= CfCheckSelection
;
2255 Statement
= MenuOption
->ThisTag
;
2256 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2257 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2258 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2259 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2264 // Keep highlight on current MenuOption
2266 Selection
->QuestionId
= Statement
->QuestionId
;
2268 switch (Statement
->Operand
) {
2269 case EFI_IFR_REF_OP
:
2270 if (Statement
->RefDevicePath
!= 0) {
2272 // Goto another Hii Package list
2274 ControlFlag
= CfUiReset
;
2275 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2277 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2278 if (StringPtr
== NULL
) {
2280 // No device path string not found, exit
2282 Selection
->Action
= UI_ACTION_EXIT
;
2283 Selection
->Statement
= NULL
;
2286 BufferSize
= StrLen (StringPtr
) / 2;
2287 DevicePath
= AllocatePool (BufferSize
);
2289 HexStringToBuffer ((UINT8
*) DevicePath
, &BufferSize
, StringPtr
);
2290 Selection
->Handle
= HiiLibDevicePathToHiiHandle (DevicePath
);
2291 if (Selection
->Handle
== NULL
) {
2293 // If target Hii Handle not found, exit
2295 Selection
->Action
= UI_ACTION_EXIT
;
2296 Selection
->Statement
= NULL
;
2300 gBS
->FreePool (StringPtr
);
2301 gBS
->FreePool (DevicePath
);
2303 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2304 Selection
->FormId
= Statement
->RefFormId
;
2305 Selection
->QuestionId
= Statement
->RefQuestionId
;
2306 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2308 // Goto another Formset, check for uncommitted data
2310 ControlFlag
= CfUiReset
;
2311 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2313 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2314 Selection
->FormId
= Statement
->RefFormId
;
2315 Selection
->QuestionId
= Statement
->RefQuestionId
;
2316 } else if (Statement
->RefFormId
!= 0) {
2318 // Goto another form inside this formset,
2320 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2323 // Link current form so that we can always go back when someone hits the UiPrevious
2325 UiAddMenuListEntry (Selection
);
2327 Selection
->FormId
= Statement
->RefFormId
;
2328 Selection
->QuestionId
= Statement
->RefQuestionId
;
2329 } else if (Statement
->RefQuestionId
!= 0) {
2331 // Goto another Question
2333 Selection
->QuestionId
= Statement
->RefQuestionId
;
2335 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2336 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2345 case EFI_IFR_ACTION_OP
:
2347 // Process the Config string <ConfigResp>
2349 Status
= ProcessQuestionConfig (Selection
, Statement
);
2351 if (EFI_ERROR (Status
)) {
2356 // The action button may change some Question value, so refresh the form
2358 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2361 case EFI_IFR_RESET_BUTTON_OP
:
2363 // Reset Question to default value specified by DefaultId
2365 ControlFlag
= CfUiDefault
;
2366 DefaultId
= Statement
->DefaultId
;
2371 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2373 UpdateKeyHelp (MenuOption
, TRUE
);
2374 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2376 if (EFI_ERROR (Status
)) {
2382 if (OptionString
!= NULL
) {
2383 PrintStringAt (LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ 1, MenuOption
->Row
, OptionString
);
2384 gBS
->FreePool (OptionString
);
2387 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2394 // We are going to leave current FormSet, so check uncommited data in this FormSet
2396 ControlFlag
= CfCheckSelection
;
2398 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
2400 // There is no parent menu for FrontPage
2402 Selection
->Action
= UI_ACTION_NONE
;
2403 Selection
->Statement
= MenuOption
->ThisTag
;
2408 // If NV flag is up, prompt user
2410 if (gNvUpdateRequired
) {
2411 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2413 YesResponse
= gYesResponse
[0];
2414 NoResponse
= gNoResponse
[0];
2417 CreateDialog (3, TRUE
, 0, NULL
, &Key
, gEmptyString
, gAreYouSure
, gEmptyString
);
2420 (Key
.ScanCode
!= SCAN_ESC
) &&
2421 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2422 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2426 // If the user hits the YesResponse key
2428 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2433 Selection
->Action
= UI_ACTION_NONE
;
2438 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2439 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2442 gST
->ConOut
->ClearScreen (gST
->ConOut
);
2446 ControlFlag
= CfCheckSelection
;
2447 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2448 if (MenuOption
->Sequence
!= 0) {
2450 // In the middle or tail of the Date/Time op-code set, go left.
2452 NewPos
= NewPos
->BackLink
;
2458 ControlFlag
= CfCheckSelection
;
2459 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2460 if (MenuOption
->Sequence
!= 2) {
2462 // In the middle or tail of the Date/Time op-code set, go left.
2464 NewPos
= NewPos
->ForwardLink
;
2470 ControlFlag
= CfCheckSelection
;
2472 SavedListEntry
= TopOfScreen
;
2474 if (NewPos
->BackLink
!= &Menu
) {
2477 // Adjust Date/Time position before we advance forward.
2479 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2482 // Caution that we have already rewind to the top, don't go backward in this situation.
2484 if (NewPos
->BackLink
!= &Menu
) {
2485 NewPos
= NewPos
->BackLink
;
2488 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2489 DistanceValue
= PreviousMenuOption
->Skip
;
2492 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2493 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2494 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2495 // checking can be done.
2497 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2500 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2501 // don't worry about a redraw.
2503 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2505 TopOfScreen
= NewPos
;
2508 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2509 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2510 if (Difference
> 0) {
2512 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2514 TopOfScreen
= NewPos
;
2518 if (Difference
< 0) {
2520 // We want to goto previous MenuOption, but finally we go down.
2521 // it means that we hit the begining MenuOption that can be focused
2522 // so we simply scroll to the top
2524 if (SavedListEntry
!= Menu
.ForwardLink
) {
2525 TopOfScreen
= Menu
.ForwardLink
;
2531 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2533 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2535 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2537 SavedMenuOption
= MenuOption
;
2538 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2539 if (!IsSelectable (MenuOption
)) {
2541 // If we are at the end of the list and sitting on a text op, we need to more forward
2543 ScreenOperation
= UiDown
;
2544 ControlFlag
= CfScreenOperation
;
2548 MenuOption
= SavedMenuOption
;
2553 ControlFlag
= CfCheckSelection
;
2555 if (NewPos
->BackLink
== &Menu
) {
2564 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2566 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2567 Index
= Index
- PreviousMenuOption
->Skip
;
2568 Link
= Link
->BackLink
;
2569 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2573 Difference
= MoveToNextStatement (TRUE
, &Link
);
2574 if (Difference
> 0) {
2576 // The focus MenuOption is above the TopOfScreen
2579 } else if (Difference
< 0) {
2581 // This happens when there is no MenuOption can be focused from
2582 // Current MenuOption to the first MenuOption
2584 TopOfScreen
= Menu
.ForwardLink
;
2586 Index
+= Difference
;
2587 if (Index
< TopRow
) {
2591 if (NewPos
== Link
) {
2599 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2600 // Don't do this when we are already in the first page.
2602 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2603 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2607 ControlFlag
= CfCheckSelection
;
2609 if (NewPos
->ForwardLink
== &Menu
) {
2618 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2620 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2621 Index
= Index
+ NextMenuOption
->Skip
;
2622 Link
= Link
->ForwardLink
;
2623 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2626 Index
+= MoveToNextStatement (FALSE
, &Link
);
2627 if (Index
> BottomRow
) {
2629 // There are more MenuOption needing scrolling
2634 if (NewPos
== Link
&& Index
<= BottomRow
) {
2636 // Finally we know that NewPos is the last MenuOption can be focused.
2645 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2646 // Don't do this when we are already in the last page.
2648 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2649 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2653 ControlFlag
= CfCheckSelection
;
2655 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2656 // to be one that progresses to the next set of op-codes, we need to advance to the last
2657 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2658 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2659 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2660 // the Date/Time op-code.
2662 SavedListEntry
= NewPos
;
2663 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2665 if (NewPos
->ForwardLink
!= &Menu
) {
2666 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2668 NewPos
= NewPos
->ForwardLink
;
2669 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2671 DistanceValue
+= NextMenuOption
->Skip
;
2672 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2674 // An option might be multi-line, so we need to reflect that data in the overall skip value
2676 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2678 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2679 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2680 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2681 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2687 // If we are going to scroll, update TopOfScreen
2689 if (Temp
> BottomRow
) {
2692 // Is the current top of screen a zero-advance op-code?
2693 // If so, keep moving forward till we hit a >0 advance op-code
2695 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2698 // If bottom op-code is more than one line or top op-code is more than one line
2700 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2702 // Is the bottom op-code greater than or equal in size to the top op-code?
2704 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2706 // Skip the top op-code
2708 TopOfScreen
= TopOfScreen
->ForwardLink
;
2709 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2711 OldSkipValue
= Difference
;
2713 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2716 // If we have a remainder, skip that many more op-codes until we drain the remainder
2719 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2720 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
2723 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2725 TopOfScreen
= TopOfScreen
->ForwardLink
;
2726 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2727 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
2728 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
2731 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
2732 TopOfScreen
= TopOfScreen
->ForwardLink
;
2733 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2734 Difference
= SavedMenuOption
->Skip
- Difference
;
2740 // Since we will act on this op-code in the next routine, and increment the
2741 // SkipValue, set the skips to one less than what is required.
2743 SkipValue
= Difference
- 1;
2747 // Since we will act on this op-code in the next routine, and increment the
2748 // SkipValue, set the skips to one less than what is required.
2750 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
2753 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
2754 TopOfScreen
= TopOfScreen
->ForwardLink
;
2757 SkipValue
= OldSkipValue
;
2761 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2762 // Let's set a skip flag to smoothly scroll the top of the screen.
2764 if (SavedMenuOption
->Skip
> 1) {
2765 if (SavedMenuOption
== NextMenuOption
) {
2772 TopOfScreen
= TopOfScreen
->ForwardLink
;
2774 } while (SavedMenuOption
->Skip
== 0);
2777 OldSkipValue
= SkipValue
;
2780 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
2782 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2785 SavedMenuOption
= MenuOption
;
2786 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2787 if (!IsSelectable (MenuOption
)) {
2789 // If we are at the end of the list and sitting on a text op, we need to more forward
2791 ScreenOperation
= UiUp
;
2792 ControlFlag
= CfScreenOperation
;
2796 MenuOption
= SavedMenuOption
;
2798 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2800 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2805 ControlFlag
= CfCheckSelection
;
2810 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2812 if (!EFI_ERROR (Status
)) {
2813 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2814 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2817 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
2818 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2826 ControlFlag
= CfCheckSelection
;
2828 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
2830 if (!EFI_ERROR (Status
)) {
2831 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2834 // Show NV update flag on status bar
2836 gNvUpdateRequired
= TRUE
;
2840 case CfUiNoOperation
:
2841 ControlFlag
= CfCheckSelection
;
2845 UiFreeRefreshList ();
2847 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2848 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
2849 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2850 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");