3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Implementation for UI.
30 MENU_REFRESH_ENTRY
*gMenuRefreshHead
;
33 // Search table for UiDisplayMenu()
35 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
58 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
87 Set Buffer to Value for Size bytes.
89 @param Buffer Memory to set.
90 @param Size Number of bytes to set
91 @param Value Value of the set operation.
113 Initialize Menu option list.
125 InitializeListHead (&Menu
);
130 Initialize Menu option list.
142 InitializeListHead (&gMenuList
);
147 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
149 @param Selection Menu selection.
155 UiRemoveMenuListEntry (
156 IN OUT UI_MENU_SELECTION
*Selection
159 UI_MENU_LIST
*UiMenuList
;
161 if (!IsListEmpty (&gMenuList
)) {
162 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
164 Selection
->FormId
= UiMenuList
->FormId
;
165 Selection
->QuestionId
= UiMenuList
->QuestionId
;
166 RemoveEntryList (&UiMenuList
->MenuLink
);
167 gBS
->FreePool (UiMenuList
);
173 Free Menu option linked list.
185 UI_MENU_LIST
*UiMenuList
;
187 while (!IsListEmpty (&gMenuList
)) {
188 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
189 RemoveEntryList (&UiMenuList
->MenuLink
);
190 gBS
->FreePool (UiMenuList
);
196 Add one menu entry to the linked lst
198 @param Selection Menu selection.
205 IN UI_MENU_SELECTION
*Selection
208 UI_MENU_LIST
*UiMenuList
;
210 UiMenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
211 ASSERT (UiMenuList
!= NULL
);
213 UiMenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
214 UiMenuList
->FormId
= Selection
->FormId
;
215 UiMenuList
->QuestionId
= Selection
->QuestionId
;
217 InsertHeadList (&gMenuList
, &UiMenuList
->MenuLink
);
222 Free Menu option linked list.
234 UI_MENU_OPTION
*MenuOption
;
236 while (!IsListEmpty (&Menu
)) {
237 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
238 RemoveEntryList (&MenuOption
->Link
);
241 // We allocated space for this description when we did a GetToken, free it here
243 if (MenuOption
->Skip
!= 0) {
245 // For date/time, MenuOption->Description is shared by three Menu Options
246 // Data format : [01/02/2004] [11:22:33]
247 // Line number : 0 0 1 0 0 1
249 gBS
->FreePool (MenuOption
->Description
);
251 gBS
->FreePool (MenuOption
);
257 Free Menu option linked list.
269 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
271 while (gMenuRefreshHead
!= NULL
) {
272 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
273 gBS
->FreePool (gMenuRefreshHead
);
274 gMenuRefreshHead
= OldMenuRefreshEntry
;
277 gMenuRefreshHead
= NULL
;
295 CHAR16
*OptionString
;
296 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
300 UI_MENU_SELECTION
*Selection
;
301 FORM_BROWSER_STATEMENT
*Question
;
305 if (gMenuRefreshHead
!= NULL
) {
307 MenuRefreshEntry
= gMenuRefreshHead
;
310 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
312 Selection
= MenuRefreshEntry
->Selection
;
313 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
316 // Don't update Question being edited
318 if (Question
!= MenuRefreshEntry
->Selection
->Statement
) {
320 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
321 if (EFI_ERROR (Status
)) {
325 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
327 if (OptionString
!= NULL
) {
329 // If leading spaces on OptionString - remove the spaces
331 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
334 for (Loop
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
335 OptionString
[Loop
] = OptionString
[Index
];
339 OptionString
[Loop
] = CHAR_NULL
;
341 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
342 gBS
->FreePool (OptionString
);
346 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
348 } while (MenuRefreshEntry
!= NULL
);
354 Wait for a given event to fire, or for an optional timeout to expire.
356 @param Event The event to wait for
357 @param Timeout An optional timeout value in 100 ns units.
358 @param RefreshInterval Menu refresh interval (in seconds).
360 @retval EFI_SUCCESS Event fired before Timeout expired.
361 @retval EFI_TIME_OUT Timout expired before Event fired.
365 UiWaitForSingleEvent (
367 IN UINT64 Timeout
, OPTIONAL
368 IN UINT8 RefreshInterval OPTIONAL
373 EFI_EVENT TimerEvent
;
374 EFI_EVENT WaitList
[2];
378 // Create a timer event
380 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
381 if (!EFI_ERROR (Status
)) {
383 // Set the timer event
392 // Wait for the original event or the timer
395 WaitList
[1] = TimerEvent
;
396 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
397 gBS
->CloseEvent (TimerEvent
);
400 // If the timer expired, change the return to timed out
402 if (!EFI_ERROR (Status
) && Index
== 1) {
403 Status
= EFI_TIMEOUT
;
408 // Update screen every second
410 if (RefreshInterval
== 0) {
411 Timeout
= ONE_SECOND
;
413 Timeout
= RefreshInterval
* ONE_SECOND
;
417 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
420 // Set the timer event
429 // Wait for the original event or the timer
432 WaitList
[1] = TimerEvent
;
433 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
436 // If the timer expired, update anything that needs a refresh and keep waiting
438 if (!EFI_ERROR (Status
) && Index
== 1) {
439 Status
= EFI_TIMEOUT
;
440 if (RefreshInterval
!= 0) {
445 gBS
->CloseEvent (TimerEvent
);
446 } while (Status
== EFI_TIMEOUT
);
454 Add one menu option by specified description and context.
456 @param String String description for this option.
457 @param Handle Hii handle for the package list.
458 @param Statement Statement of this Menu Option.
459 @param NumberOfLines Display lines for this Menu Option.
460 @param MenuItemCount The index for this Option in the Menu.
468 IN EFI_HII_HANDLE Handle
,
469 IN FORM_BROWSER_STATEMENT
*Statement
,
470 IN UINT16 NumberOfLines
,
471 IN UINT16 MenuItemCount
474 UI_MENU_OPTION
*MenuOption
;
480 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
482 // Add three MenuOptions for Date/Time
483 // Data format : [01/02/2004] [11:22:33]
484 // Line number : 0 0 1 0 0 1
489 if (Statement
->Storage
== NULL
) {
491 // For RTC type of date/time, set default refresh interval to be 1 second
493 if (Statement
->RefreshInterval
== 0) {
494 Statement
->RefreshInterval
= 1;
499 for (Index
= 0; Index
< Count
; Index
++) {
500 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
503 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
504 MenuOption
->Description
= String
;
505 MenuOption
->Handle
= Handle
;
506 MenuOption
->ThisTag
= Statement
;
507 MenuOption
->EntryNumber
= MenuItemCount
;
511 // Override LineNumber for the MenuOption in Date/Time sequence
513 MenuOption
->Skip
= 1;
515 MenuOption
->Skip
= NumberOfLines
;
517 MenuOption
->Sequence
= Index
;
519 if (Statement
->GrayOutExpression
!= NULL
) {
520 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
523 if ((Statement
->ValueExpression
!= NULL
) ||
524 (Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
)) {
525 MenuOption
->ReadOnly
= TRUE
;
528 InsertTailList (&Menu
, &MenuOption
->Link
);
534 Routine used to abstract a generic dialog interface and return the selected key or string
536 @param NumberOfLines The number of lines for the dialog box
537 @param HotKey Defines whether a single character is parsed
538 (TRUE) and returned in KeyValue or a string is
539 returned in StringBuffer. Two special characters
540 are considered when entering a string, a SCAN_ESC
541 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
542 string input and returns
543 @param MaximumStringSize The maximum size in bytes of a typed in string
544 (each character is a CHAR16) and the minimum
545 string returned is two bytes
546 @param StringBuffer The passed in pointer to the buffer which will
547 hold the typed in string if HotKey is FALSE
548 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
549 @param String Pointer to the first string in the list
550 @param ... A series of (quantity == NumberOfLines) text
551 strings which will be used to construct the dialog
554 @retval EFI_SUCCESS Displayed dialog and received user interaction
555 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
556 (StringBuffer == NULL) && (HotKey == FALSE))
557 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
562 IN UINTN NumberOfLines
,
564 IN UINTN MaximumStringSize
,
565 OUT CHAR16
*StringBuffer
,
566 OUT EFI_INPUT_KEY
*KeyValue
,
576 CHAR16
*BufferedString
;
583 BOOLEAN SelectionComplete
;
585 UINTN CurrentAttribute
;
586 UINTN DimensionsWidth
;
587 UINTN DimensionsHeight
;
589 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
590 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
592 SelectionComplete
= FALSE
;
594 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
595 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
596 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
599 ASSERT (BufferedString
);
601 VA_START (Marker
, String
);
604 // Zero the outgoing buffer
606 ZeroMem (StringBuffer
, MaximumStringSize
);
609 if (KeyValue
== NULL
) {
610 return EFI_INVALID_PARAMETER
;
613 if (StringBuffer
== NULL
) {
614 return EFI_INVALID_PARAMETER
;
620 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
622 LargestString
= (GetStringWidth (String
) / 2);
624 if (*String
== L
' ') {
628 // Determine the largest string in the dialog box
629 // Notice we are starting with 1 since String is the first string
631 for (Count
= 1; Count
< NumberOfLines
; Count
++) {
632 StackString
= VA_ARG (Marker
, CHAR16
*);
634 if (StackString
[0] == L
' ') {
635 InputOffset
= Count
+ 1;
638 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
640 // Size of the string visually and subtract the width by one for the null-terminator
642 LargestString
= (GetStringWidth (StackString
) / 2);
646 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
647 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
654 CreateSharedPopUp (LargestString
, NumberOfLines
, &String
);
657 // Take the first key typed and report it back?
660 Status
= WaitForKeyStroke (&Key
);
661 ASSERT_EFI_ERROR (Status
);
662 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
666 Status
= WaitForKeyStroke (&Key
);
668 switch (Key
.UnicodeChar
) {
670 switch (Key
.ScanCode
) {
672 gBS
->FreePool (TempString
);
673 gBS
->FreePool (BufferedString
);
674 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
675 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
676 return EFI_DEVICE_ERROR
;
684 case CHAR_CARRIAGE_RETURN
:
685 SelectionComplete
= TRUE
;
686 gBS
->FreePool (TempString
);
687 gBS
->FreePool (BufferedString
);
688 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
689 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
694 if (StringBuffer
[0] != CHAR_NULL
) {
695 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
696 TempString
[Index
] = StringBuffer
[Index
];
699 // Effectively truncate string by 1 character
701 TempString
[Index
- 1] = CHAR_NULL
;
702 StrCpy (StringBuffer
, TempString
);
707 // If it is the beginning of the string, don't worry about checking maximum limits
709 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
710 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
711 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
712 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
713 KeyPad
[0] = Key
.UnicodeChar
;
714 KeyPad
[1] = CHAR_NULL
;
715 StrCat (StringBuffer
, KeyPad
);
716 StrCat (TempString
, KeyPad
);
719 // If the width of the input string is now larger than the screen, we nee to
720 // adjust the index to start printing portions of the string
722 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
724 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
726 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
727 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
732 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
733 BufferedString
[Count
] = StringBuffer
[Index
];
736 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
739 } while (!SelectionComplete
);
742 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
743 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
749 IN UINTN RequestedWidth
,
750 IN UINTN NumberOfLines
,
751 IN CHAR16
**ArrayOfStrings
762 UINTN DimensionsWidth
;
763 UINTN DimensionsHeight
;
765 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
766 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
770 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
772 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
773 RequestedWidth
= DimensionsWidth
- 2;
777 // Subtract the PopUp width from total Columns, allow for one space extra on
778 // each end plus a border.
780 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
781 End
= Start
+ RequestedWidth
+ 1;
783 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
784 Bottom
= Top
+ NumberOfLines
+ 2;
786 Character
= BOXDRAW_DOWN_RIGHT
;
787 PrintCharAt (Start
, Top
, Character
);
788 Character
= BOXDRAW_HORIZONTAL
;
789 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
790 PrintChar (Character
);
793 Character
= BOXDRAW_DOWN_LEFT
;
794 PrintChar (Character
);
795 Character
= BOXDRAW_VERTICAL
;
796 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++) {
797 String
= ArrayOfStrings
[Count
];
801 // This will clear the background of the line - we never know who might have been
802 // here before us. This differs from the next clear in that it used the non-reverse
803 // video for normal printing.
805 if (GetStringWidth (String
) / 2 > 1) {
806 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
810 // Passing in a space results in the assumption that this is where typing will occur
812 if (String
[0] == L
' ') {
813 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
817 // Passing in a NULL results in a blank space
819 if (String
[0] == CHAR_NULL
) {
820 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
824 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
828 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
829 PrintCharAt (Start
, Index
+ 1, Character
);
830 PrintCharAt (End
- 1, Index
+ 1, Character
);
833 Character
= BOXDRAW_UP_RIGHT
;
834 PrintCharAt (Start
, Bottom
- 1, Character
);
835 Character
= BOXDRAW_HORIZONTAL
;
836 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
837 PrintChar (Character
);
840 Character
= BOXDRAW_UP_LEFT
;
841 PrintChar (Character
);
846 IN UINTN RequestedWidth
,
847 IN UINTN NumberOfLines
,
848 IN CHAR16
*ArrayOfStrings
,
852 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, &ArrayOfStrings
);
857 Update status bar on the bottom of menu.
859 @param MessageType The type of message to be shown.
860 @param Flags The flags in Question header.
861 @param State Set or clear.
868 IN UINTN MessageType
,
874 STATIC BOOLEAN InputError
;
875 CHAR16
*NvUpdateMessage
;
876 CHAR16
*InputErrorMessage
;
878 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
879 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
881 switch (MessageType
) {
884 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
886 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
887 gScreenDimensions
.BottomRow
- 1,
892 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
893 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
894 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
901 case NV_UPDATE_REQUIRED
:
902 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
904 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
906 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
907 gScreenDimensions
.BottomRow
- 1,
910 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
912 gNvUpdateRequired
= TRUE
;
914 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
915 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
917 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
918 gScreenDimensions
.BottomRow
- 1,
923 gNvUpdateRequired
= FALSE
;
928 case REFRESH_STATUS_BAR
:
930 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
933 if (gNvUpdateRequired
) {
934 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
942 gBS
->FreePool (InputErrorMessage
);
943 gBS
->FreePool (NvUpdateMessage
);
949 Get the supported width for a particular op-code
951 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
952 @param Handle The handle in the HII database being used
954 @return Returns the number of CHAR16 characters that is support.
959 IN FORM_BROWSER_STATEMENT
*Statement
,
960 IN EFI_HII_HANDLE Handle
970 // See if the second text parameter is really NULL
972 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
973 String
= GetToken (Statement
->TextTwo
, Handle
);
974 Size
= StrLen (String
);
975 gBS
->FreePool (String
);
978 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
979 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
980 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
981 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
982 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
984 // Allow a wide display if text op-code and no secondary text op-code
986 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
988 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
990 Width
= (UINT16
) gPromptBlockWidth
;
993 if (Statement
->InSubtitle
) {
994 Width
-= SUBTITLE_INDENT
;
1002 Will copy LineWidth amount of a string in the OutputString buffer and return the
1003 number of CHAR16 characters that were copied into the OutputString buffer.
1005 @param InputString String description for this option.
1006 @param LineWidth Width of the desired string to extract in CHAR16
1008 @param Index Where in InputString to start the copy process
1009 @param OutputString Buffer to copy the string into
1011 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1016 IN CHAR16
*InputString
,
1017 IN UINT16 LineWidth
,
1018 IN OUT UINTN
*Index
,
1019 OUT CHAR16
**OutputString
1022 static BOOLEAN Finished
;
1034 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1037 // Ensure we have got a valid buffer
1039 if (*OutputString
!= NULL
) {
1042 //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.
1043 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1045 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1046 *Index
= *Index
+ 2;
1050 // Fast-forward the string and see if there is a carriage-return in the string
1052 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1056 // Copy the desired LineWidth of data to the output buffer.
1057 // Also make sure that we don't copy more than the string.
1058 // Also make sure that if there are linefeeds, we account for them.
1060 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1061 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1064 // Convert to CHAR16 value and show that we are done with this operation
1066 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1067 if (LineWidth
!= 0) {
1071 if (Count2
== LineWidth
) {
1073 // Rewind the string from the maximum size until we see a space to break the line
1075 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1077 if (LineWidth
== 0) {
1085 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1088 // If currently pointing to a space, increment the index to the first non-space character
1091 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1095 *Index
= (UINT16
) (*Index
+ LineWidth
);
1104 Update display lines for a Menu Option.
1106 @param MenuOption The MenuOption to be checked.
1108 @retval TRUE This Menu Option is selectable.
1109 @retval FALSE This Menu Option could not be selected.
1113 UpdateOptionSkipLines (
1114 IN UI_MENU_SELECTION
*Selection
,
1115 IN UI_MENU_OPTION
*MenuOption
,
1116 IN CHAR16
**OptionalString
,
1124 CHAR16
*OutputString
;
1125 CHAR16
*OptionString
;
1128 OptionString
= *OptionalString
;
1129 OutputString
= NULL
;
1131 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1133 if (OptionString
!= NULL
) {
1134 Width
= (UINT16
) gOptionBlockWidth
;
1138 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1140 // If there is more string to process print on the next row and increment the Skip value
1142 if (StrLen (&OptionString
[Index
])) {
1143 if (SkipValue
== 0) {
1146 // Since the Number of lines for this menu entry may or may not be reflected accurately
1147 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1148 // some testing to ensure we are keeping this in-sync.
1150 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1152 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1158 gBS
->FreePool (OutputString
);
1159 if (SkipValue
!= 0) {
1167 *OptionalString
= OptionString
;
1172 Check whether this Menu Option could be highlighted.
1174 @param MenuOption The MenuOption to be checked.
1176 @retval TRUE This Menu Option is selectable.
1177 @retval FALSE This Menu Option could not be selected.
1183 UI_MENU_OPTION
*MenuOption
1186 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1187 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1196 Determine if the menu is the last menu that can be selected.
1198 @param Direction the scroll direction. False is down. True is up.
1200 @return FALSE -- the menu isn't the last menu that can be selected.
1201 @return TRUE -- the menu is the last menu that can be selected.
1207 IN BOOLEAN Direction
,
1208 IN LIST_ENTRY
*CurrentPos
1212 UI_MENU_OPTION
*MenuOption
;
1214 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1216 if (Temp
== &Menu
) {
1220 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1221 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1222 if (IsSelectable (MenuOption
)) {
1232 Move to next selectable statement.
1234 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1235 @param CurrentPosition Current position.
1237 @return The row distance from current MenuOption to next selectable MenuOption.
1242 MoveToNextStatement (
1244 IN OUT LIST_ENTRY
**CurrentPosition
1250 UI_MENU_OPTION
*NextMenuOption
;
1253 Pos
= *CurrentPosition
;
1257 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1258 if (IsSelectable (NextMenuOption
)) {
1261 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1265 Distance
+= NextMenuOption
->Skip
;
1266 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1271 // If we hit end there is still no statement can be focused,
1272 // we go backwards to find the statement can be focused.
1275 Pos
= *CurrentPosition
;
1278 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1279 if (IsSelectable (NextMenuOption
)) {
1282 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1286 Distance
-= NextMenuOption
->Skip
;
1287 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1291 *CurrentPosition
= &NextMenuOption
->Link
;
1297 Adjust Data and Time position accordingly.
1298 Data format : [01/02/2004] [11:22:33]
1299 Line number : 0 0 1 0 0 1
1301 @param DirectionUp the up or down direction. False is down. True is
1303 @param CurrentPosition Current position. On return: Point to the last
1304 Option (Year or Second) if up; Point to the first
1305 Option (Month or Hour) if down.
1307 @return Return line number to pad. It is possible that we stand on a zero-advance
1308 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1313 AdjustDateAndTimePosition (
1314 IN BOOLEAN DirectionUp
,
1315 IN OUT LIST_ENTRY
**CurrentPosition
1319 LIST_ENTRY
*NewPosition
;
1320 UI_MENU_OPTION
*MenuOption
;
1321 UINTN PadLineNumber
;
1324 NewPosition
= *CurrentPosition
;
1325 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1327 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1328 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1330 // Calculate the distance from current position to the last Date/Time MenuOption
1333 while (MenuOption
->Skip
== 0) {
1335 NewPosition
= NewPosition
->ForwardLink
;
1336 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1340 NewPosition
= *CurrentPosition
;
1343 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1344 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1345 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1346 // checking can be done.
1348 while (Count
++ < 2) {
1349 NewPosition
= NewPosition
->BackLink
;
1353 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1354 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1355 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1356 // checking can be done.
1358 while (Count
-- > 0) {
1359 NewPosition
= NewPosition
->ForwardLink
;
1363 *CurrentPosition
= NewPosition
;
1366 return PadLineNumber
;
1371 Display menu and wait for user to select one menu option, then return it.
1372 If AutoBoot is enabled, then if user doesn't select any option,
1373 after period of time, it will automatically return the first menu option.
1376 @return Return the pointer of the menu which selected,
1377 @return otherwise return NULL.
1382 IN OUT UI_MENU_SELECTION
*Selection
1388 UINTN DistanceValue
;
1400 CHAR16
*OptionString
;
1401 CHAR16
*OutputString
;
1402 CHAR16
*FormattedString
;
1412 LIST_ENTRY
*TopOfScreen
;
1413 LIST_ENTRY
*SavedListEntry
;
1414 UI_MENU_OPTION
*MenuOption
;
1415 UI_MENU_OPTION
*NextMenuOption
;
1416 UI_MENU_OPTION
*SavedMenuOption
;
1417 UI_MENU_OPTION
*PreviousMenuOption
;
1418 UI_CONTROL_FLAG ControlFlag
;
1419 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1420 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1421 UI_SCREEN_OPERATION ScreenOperation
;
1422 UINT8 MinRefreshInterval
;
1425 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1426 FORM_BROWSER_STATEMENT
*Statement
;
1428 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1430 Status
= EFI_SUCCESS
;
1431 FormattedString
= NULL
;
1432 OptionString
= NULL
;
1433 ScreenOperation
= UiNoOperation
;
1435 MinRefreshInterval
= 0;
1438 OutputString
= NULL
;
1443 MenuRefreshEntry
= gMenuRefreshHead
;
1445 NextMenuOption
= NULL
;
1446 PreviousMenuOption
= NULL
;
1447 SavedMenuOption
= NULL
;
1449 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1451 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
1452 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1453 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1455 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1456 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1459 Col
= LocalScreen
.LeftColumn
;
1460 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1462 Selection
->TopRow
= TopRow
;
1463 Selection
->BottomRow
= BottomRow
;
1464 Selection
->PromptCol
= Col
;
1465 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1466 Selection
->Statement
= NULL
;
1468 TopOfScreen
= Menu
.ForwardLink
;
1473 // Get user's selection
1475 NewPos
= Menu
.ForwardLink
;
1477 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1478 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1480 ControlFlag
= CfInitialization
;
1481 Selection
->Action
= UI_ACTION_NONE
;
1483 switch (ControlFlag
) {
1484 case CfInitialization
:
1485 if (IsListEmpty (&Menu
)) {
1486 ControlFlag
= CfReadKey
;
1488 ControlFlag
= CfCheckSelection
;
1492 case CfCheckSelection
:
1493 if (Selection
->Action
!= UI_ACTION_NONE
) {
1494 ControlFlag
= CfExit
;
1496 ControlFlag
= CfRepaint
;
1501 ControlFlag
= CfRefreshHighLight
;
1515 LocalScreen
.LeftColumn
,
1516 LocalScreen
.RightColumn
,
1517 TopRow
- SCROLL_ARROW_HEIGHT
,
1518 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1519 FIELD_TEXT
| FIELD_BACKGROUND
1522 UiFreeRefreshList ();
1523 MinRefreshInterval
= 0;
1525 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1526 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1527 MenuOption
->Row
= Row
;
1528 MenuOption
->Col
= Col
;
1529 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1531 Statement
= MenuOption
->ThisTag
;
1532 if (Statement
->InSubtitle
) {
1533 MenuOption
->Col
+= SUBTITLE_INDENT
;
1536 if (MenuOption
->GrayOut
) {
1537 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1539 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1540 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1544 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1547 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1548 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1549 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1552 // If there is more string to process print on the next row and increment the Skip value
1554 if (StrLen (&MenuOption
->Description
[Index
])) {
1560 gBS
->FreePool (OutputString
);
1569 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1570 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1572 if (OptionString
!= NULL
) {
1573 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1575 // If leading spaces on OptionString - remove the spaces
1577 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1578 MenuOption
->OptCol
++;
1581 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1582 OptionString
[Count
] = OptionString
[Index
];
1586 OptionString
[Count
] = CHAR_NULL
;
1590 // If Question request refresh, register the op-code
1592 if (Statement
->RefreshInterval
!= 0) {
1594 // Menu will be refreshed at minimal interval of all Questions
1595 // which have refresh request
1597 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1598 MinRefreshInterval
= Statement
->RefreshInterval
;
1601 if (gMenuRefreshHead
== NULL
) {
1602 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1603 ASSERT (MenuRefreshEntry
!= NULL
);
1604 MenuRefreshEntry
->MenuOption
= MenuOption
;
1605 MenuRefreshEntry
->Selection
= Selection
;
1606 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1607 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1608 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1609 gMenuRefreshHead
= MenuRefreshEntry
;
1612 // Advance to the last entry
1614 for (MenuRefreshEntry
= gMenuRefreshHead
;
1615 MenuRefreshEntry
->Next
!= NULL
;
1616 MenuRefreshEntry
= MenuRefreshEntry
->Next
1619 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1620 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1621 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1622 MenuRefreshEntry
->MenuOption
= MenuOption
;
1623 MenuRefreshEntry
->Selection
= Selection
;
1624 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1625 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1626 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1630 Width
= (UINT16
) gOptionBlockWidth
;
1633 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1634 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1635 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1638 // If there is more string to process print on the next row and increment the Skip value
1640 if (StrLen (&OptionString
[Index
])) {
1644 // Since the Number of lines for this menu entry may or may not be reflected accurately
1645 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1646 // some testing to ensure we are keeping this in-sync.
1648 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1650 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1656 gBS
->FreePool (OutputString
);
1665 gBS
->FreePool (OptionString
);
1668 // If this is a text op with secondary text information
1670 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1671 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1673 Width
= (UINT16
) gOptionBlockWidth
;
1676 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1677 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1678 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1681 // If there is more string to process print on the next row and increment the Skip value
1683 if (StrLen (&StringPtr
[Index
])) {
1687 // Since the Number of lines for this menu entry may or may not be reflected accurately
1688 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1689 // some testing to ensure we are keeping this in-sync.
1691 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1693 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1699 gBS
->FreePool (OutputString
);
1706 gBS
->FreePool (StringPtr
);
1710 // Need to handle the bottom of the display
1712 if (MenuOption
->Skip
> 1) {
1713 Row
+= MenuOption
->Skip
- SkipValue
;
1716 Row
+= MenuOption
->Skip
;
1719 if (Row
> BottomRow
) {
1720 if (!ValueIsScroll (FALSE
, Link
)) {
1724 Row
= BottomRow
+ 1;
1729 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
1734 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1736 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1737 TopRow
- SCROLL_ARROW_HEIGHT
,
1741 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1745 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1747 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1748 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1752 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1759 case CfRefreshHighLight
:
1761 // MenuOption: Last menu option that need to remove hilight
1762 // MenuOption is set to NULL in Repaint
1763 // NewPos: Current menu option that need to hilight
1765 ControlFlag
= CfUpdateHelpString
;
1768 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1769 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1771 SavedValue
= Repaint
;
1774 if (Selection
->QuestionId
!= 0) {
1775 NewPos
= Menu
.ForwardLink
;
1776 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1778 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
1779 NewPos
= NewPos
->ForwardLink
;
1780 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1782 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
1784 // Target Question found, find its MenuOption
1788 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
1789 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1790 Index
+= SavedMenuOption
->Skip
;
1791 Link
= Link
->ForwardLink
;
1794 if (Link
!= NewPos
|| Index
> BottomRow
) {
1796 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1799 for (Index
= TopRow
; Index
<= BottomRow
; ) {
1800 Link
= Link
->BackLink
;
1801 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1802 Index
+= SavedMenuOption
->Skip
;
1804 TopOfScreen
= Link
->ForwardLink
;
1808 ControlFlag
= CfRepaint
;
1813 // Target Question not found, highlight the default menu option
1815 NewPos
= TopOfScreen
;
1818 Selection
->QuestionId
= 0;
1821 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
1822 if (MenuOption
!= NULL
) {
1824 // Remove highlight on last Menu Option
1826 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1827 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1828 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1829 if (OptionString
!= NULL
) {
1830 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1831 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
1834 // If leading spaces on OptionString - remove the spaces
1836 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1839 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1840 OptionString
[Count
] = OptionString
[Index
];
1844 OptionString
[Count
] = CHAR_NULL
;
1847 Width
= (UINT16
) gOptionBlockWidth
;
1848 OriginalRow
= MenuOption
->Row
;
1850 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1851 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1852 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1855 // If there is more string to process print on the next row and increment the Skip value
1857 if (StrLen (&OptionString
[Index
])) {
1861 gBS
->FreePool (OutputString
);
1864 MenuOption
->Row
= OriginalRow
;
1866 gBS
->FreePool (OptionString
);
1869 if (MenuOption
->GrayOut
) {
1870 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1871 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1872 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1875 OriginalRow
= MenuOption
->Row
;
1876 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
1878 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1879 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1880 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1883 // If there is more string to process print on the next row and increment the Skip value
1885 if (StrLen (&MenuOption
->Description
[Index
])) {
1889 gBS
->FreePool (OutputString
);
1892 MenuOption
->Row
= OriginalRow
;
1893 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1899 // This is only possible if we entered this page and the first menu option is
1900 // a "non-menu" item. In that case, force it UiDown
1902 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1903 if (!IsSelectable (MenuOption
)) {
1904 ASSERT (ScreenOperation
== UiNoOperation
);
1905 ScreenOperation
= UiDown
;
1906 ControlFlag
= CfScreenOperation
;
1911 // This is the current selected statement
1913 Statement
= MenuOption
->ThisTag
;
1914 Selection
->Statement
= Statement
;
1917 // Set reverse attribute
1919 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
1920 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1923 // Assuming that we have a refresh linked-list created, lets annotate the
1924 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1925 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1927 if (gMenuRefreshHead
!= NULL
) {
1928 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
1929 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1930 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
1931 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
1936 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1937 if (OptionString
!= NULL
) {
1938 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1940 // If leading spaces on OptionString - remove the spaces
1942 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1945 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1946 OptionString
[Count
] = OptionString
[Index
];
1950 OptionString
[Count
] = CHAR_NULL
;
1952 Width
= (UINT16
) gOptionBlockWidth
;
1954 OriginalRow
= MenuOption
->Row
;
1956 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1957 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1958 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1961 // If there is more string to process print on the next row and increment the Skip value
1963 if (StrLen (&OptionString
[Index
])) {
1967 gBS
->FreePool (OutputString
);
1970 MenuOption
->Row
= OriginalRow
;
1972 gBS
->FreePool (OptionString
);
1975 OriginalRow
= MenuOption
->Row
;
1977 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1979 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1980 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1981 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1984 // If there is more string to process print on the next row and increment the Skip value
1986 if (StrLen (&MenuOption
->Description
[Index
])) {
1990 gBS
->FreePool (OutputString
);
1993 MenuOption
->Row
= OriginalRow
;
1998 if (((NewPos
->ForwardLink
!= &Menu
) && (ScreenOperation
== UiDown
)) ||
1999 ((NewPos
->BackLink
!= &Menu
) && (ScreenOperation
== UiUp
)) ||
2000 (ScreenOperation
== UiNoOperation
)
2002 UpdateKeyHelp (MenuOption
, FALSE
);
2005 // Clear reverse attribute
2007 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2010 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2011 // if we didn't break halfway when process CfRefreshHighLight.
2013 Repaint
= SavedValue
;
2016 case CfUpdateHelpString
:
2017 ControlFlag
= CfPrepareToReadKey
;
2019 if ((Repaint
|| NewLine
) && (gClassOfVfr
!= EFI_GENERAL_APPLICATION_SUBCLASS
)) {
2021 // Don't print anything if it is a NULL help token
2023 if (MenuOption
->ThisTag
->Help
== 0) {
2026 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2029 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2031 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2033 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2035 // Pad String with spaces to simulate a clearing of the previous line
2037 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2038 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2042 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2044 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2049 // Reset this flag every time we finish using it.
2055 case CfPrepareToReadKey
:
2056 ControlFlag
= CfReadKey
;
2057 ScreenOperation
= UiNoOperation
;
2061 ControlFlag
= CfScreenOperation
;
2064 // Wait for user's selection
2067 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2068 } while (Status
== EFI_TIMEOUT
);
2070 if (Status
== EFI_TIMEOUT
) {
2071 Key
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
2073 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2075 // if we encounter error, continue to read another key in.
2077 if (EFI_ERROR (Status
)) {
2078 ControlFlag
= CfReadKey
;
2083 if (IsListEmpty (&Menu
) && Key
.UnicodeChar
!= CHAR_NULL
) {
2085 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2090 switch (Key
.UnicodeChar
) {
2091 case CHAR_CARRIAGE_RETURN
:
2092 ScreenOperation
= UiSelect
;
2097 // We will push the adjustment of these numeric values directly to the input handler
2098 // NOTE: we won't handle manual input numeric
2102 Statement
= MenuOption
->ThisTag
;
2103 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2104 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2105 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2107 if (Key
.UnicodeChar
== '+') {
2108 gDirection
= SCAN_RIGHT
;
2110 gDirection
= SCAN_LEFT
;
2112 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2113 SafeFreePool (OptionString
);
2118 ScreenOperation
= UiUp
;
2123 ScreenOperation
= UiDown
;
2127 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
2128 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2129 ScreenOperation
= UiSelect
;
2135 if (((Key
.ScanCode
== SCAN_F1
) && ((gFunctionKeySetting
& FUNCTION_ONE
) != FUNCTION_ONE
)) ||
2136 ((Key
.ScanCode
== SCAN_F2
) && ((gFunctionKeySetting
& FUNCTION_TWO
) != FUNCTION_TWO
)) ||
2137 ((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2138 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2141 // If the function key has been disabled, just ignore the key.
2144 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2145 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2146 if (Key
.ScanCode
== SCAN_F9
) {
2148 // Reset to standard default
2150 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2152 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2161 case CfScreenOperation
:
2162 if (ScreenOperation
!= UiPrevious
&& ScreenOperation
!= UiReset
) {
2164 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2165 // ignore the selection and go back to reading keys.
2167 if (IsListEmpty (&Menu
)) {
2168 ControlFlag
= CfReadKey
;
2172 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2174 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2175 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2176 if (IsSelectable (NextMenuOption
)) {
2181 if (Link
== &Menu
) {
2182 ControlFlag
= CfPrepareToReadKey
;
2185 } else if (ScreenOperation
== UiReset
) {
2187 // Press ESC to exit FormSet
2189 Selection
->Action
= UI_ACTION_EXIT
;
2190 Selection
->Statement
= NULL
;
2194 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2197 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2198 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2205 ControlFlag
= CfCheckSelection
;
2207 if (IsListEmpty (&gMenuList
)) {
2208 Selection
->Action
= UI_ACTION_NONE
;
2209 if (IsListEmpty (&Menu
)) {
2210 ControlFlag
= CfReadKey
;
2216 // Remove the Cached page entry
2218 UiRemoveMenuListEntry (Selection
);
2220 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2221 Selection
->Statement
= NULL
;
2225 ControlFlag
= CfCheckSelection
;
2227 Statement
= MenuOption
->ThisTag
;
2228 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2229 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2230 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2231 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2236 // Keep highlight on current MenuOption
2238 Selection
->QuestionId
= Statement
->QuestionId
;
2240 switch (Statement
->Operand
) {
2241 case EFI_IFR_REF_OP
:
2242 if (Statement
->RefDevicePath
!= 0) {
2244 // Goto another Hii Package list
2246 ControlFlag
= CfUiReset
;
2247 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2249 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2250 if (StringPtr
== NULL
) {
2252 // No device path string not found, exit
2254 Selection
->Action
= UI_ACTION_EXIT
;
2255 Selection
->Statement
= NULL
;
2258 BufferSize
= StrLen (StringPtr
) / 4;
2259 DevicePath
= AllocatePool (BufferSize
);
2261 HexStringToBuffer ((UINT8
*) DevicePath
, &BufferSize
, StringPtr
);
2262 Selection
->Handle
= HiiLibDevicePathToHiiHandle (DevicePath
);
2263 if (Selection
->Handle
== NULL
) {
2265 // If target Hii Handle not found, exit
2267 Selection
->Action
= UI_ACTION_EXIT
;
2268 Selection
->Statement
= NULL
;
2272 gBS
->FreePool (StringPtr
);
2273 gBS
->FreePool (DevicePath
);
2275 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2276 Selection
->FormId
= Statement
->RefFormId
;
2277 Selection
->QuestionId
= Statement
->RefQuestionId
;
2278 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2280 // Goto another Formset, check for uncommitted data
2282 ControlFlag
= CfUiReset
;
2283 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2285 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2286 Selection
->FormId
= Statement
->RefFormId
;
2287 Selection
->QuestionId
= Statement
->RefQuestionId
;
2288 } else if (Statement
->RefFormId
!= 0) {
2290 // Goto another form inside this formset,
2292 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2295 // Link current form so that we can always go back when someone hits the UiPrevious
2297 UiAddMenuListEntry (Selection
);
2299 Selection
->FormId
= Statement
->RefFormId
;
2300 Selection
->QuestionId
= Statement
->RefQuestionId
;
2301 } else if (Statement
->RefQuestionId
!= 0) {
2303 // Goto another Question
2305 Selection
->QuestionId
= Statement
->RefQuestionId
;
2307 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
)) {
2308 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2317 case EFI_IFR_ACTION_OP
:
2319 // Process the Config string <ConfigResp>
2321 Status
= ProcessQuestionConfig (Selection
, Statement
);
2323 if (EFI_ERROR (Status
)) {
2328 // The action button may change some Question value, so refresh the form
2330 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2333 case EFI_IFR_RESET_BUTTON_OP
:
2335 // Reset Question to default value specified by DefaultId
2337 ControlFlag
= CfUiDefault
;
2338 DefaultId
= Statement
->DefaultId
;
2343 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2345 UpdateKeyHelp (MenuOption
, TRUE
);
2346 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2348 if (EFI_ERROR (Status
)) {
2354 if (OptionString
!= NULL
) {
2355 PrintStringAt (LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ 1, MenuOption
->Row
, OptionString
);
2356 gBS
->FreePool (OptionString
);
2359 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2366 // We are going to leave current FormSet, so check uncommited data in this FormSet
2368 ControlFlag
= CfCheckSelection
;
2370 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
2372 // There is no parent menu for FrontPage
2374 Selection
->Action
= UI_ACTION_NONE
;
2375 Selection
->Statement
= MenuOption
->ThisTag
;
2380 // If NV flag is up, prompt user
2382 if (gNvUpdateRequired
) {
2383 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2385 YesResponse
= gYesResponse
[0];
2386 NoResponse
= gNoResponse
[0];
2389 CreateDialog (3, TRUE
, 0, NULL
, &Key
, gEmptyString
, gAreYouSure
, gEmptyString
);
2392 (Key
.ScanCode
!= SCAN_ESC
) &&
2393 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2394 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2398 // If the user hits the YesResponse key
2400 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2405 Selection
->Action
= UI_ACTION_NONE
;
2410 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2411 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2414 gST
->ConOut
->ClearScreen (gST
->ConOut
);
2418 ControlFlag
= CfCheckSelection
;
2419 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2420 if (MenuOption
->Sequence
!= 0) {
2422 // In the middle or tail of the Date/Time op-code set, go left.
2424 NewPos
= NewPos
->BackLink
;
2430 ControlFlag
= CfCheckSelection
;
2431 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2432 if (MenuOption
->Sequence
!= 2) {
2434 // In the middle or tail of the Date/Time op-code set, go left.
2436 NewPos
= NewPos
->ForwardLink
;
2442 ControlFlag
= CfCheckSelection
;
2444 SavedListEntry
= TopOfScreen
;
2446 if (NewPos
->BackLink
!= &Menu
) {
2449 // Adjust Date/Time position before we advance forward.
2451 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2454 // Caution that we have already rewind to the top, don't go backward in this situation.
2456 if (NewPos
->BackLink
!= &Menu
) {
2457 NewPos
= NewPos
->BackLink
;
2460 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2461 DistanceValue
= PreviousMenuOption
->Skip
;
2464 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2465 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2466 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2467 // checking can be done.
2469 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2472 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2473 // don't worry about a redraw.
2475 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2477 TopOfScreen
= NewPos
;
2480 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2481 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2482 if (Difference
> 0) {
2484 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2486 TopOfScreen
= NewPos
;
2490 if (Difference
< 0) {
2492 // We want to goto previous MenuOption, but finally we go down.
2493 // it means that we hit the begining MenuOption that can be focused
2494 // so we simply scroll to the top
2496 if (SavedListEntry
!= Menu
.ForwardLink
) {
2497 TopOfScreen
= Menu
.ForwardLink
;
2503 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2505 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2507 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2509 SavedMenuOption
= MenuOption
;
2510 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2511 if (!IsSelectable (MenuOption
)) {
2513 // If we are at the end of the list and sitting on a text op, we need to more forward
2515 ScreenOperation
= UiDown
;
2516 ControlFlag
= CfScreenOperation
;
2520 MenuOption
= SavedMenuOption
;
2525 ControlFlag
= CfCheckSelection
;
2527 if (NewPos
->BackLink
== &Menu
) {
2536 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2538 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2539 Index
= Index
- PreviousMenuOption
->Skip
;
2540 Link
= Link
->BackLink
;
2541 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2545 Difference
= MoveToNextStatement (TRUE
, &Link
);
2546 if (Difference
> 0) {
2548 // The focus MenuOption is above the TopOfScreen
2551 } else if (Difference
< 0) {
2553 // This happens when there is no MenuOption can be focused from
2554 // Current MenuOption to the first MenuOption
2556 TopOfScreen
= Menu
.ForwardLink
;
2558 Index
+= Difference
;
2559 if (Index
< TopRow
) {
2563 if (NewPos
== Link
) {
2571 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2572 // Don't do this when we are already in the first page.
2574 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2575 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2579 ControlFlag
= CfCheckSelection
;
2581 if (NewPos
->ForwardLink
== &Menu
) {
2590 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2592 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2593 Index
= Index
+ NextMenuOption
->Skip
;
2594 Link
= Link
->ForwardLink
;
2595 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2598 Index
+= MoveToNextStatement (FALSE
, &Link
);
2599 if (Index
> BottomRow
) {
2601 // There are more MenuOption needing scrolling
2606 if (NewPos
== Link
&& Index
<= BottomRow
) {
2608 // Finally we know that NewPos is the last MenuOption can be focused.
2617 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2618 // Don't do this when we are already in the last page.
2620 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2621 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2625 ControlFlag
= CfCheckSelection
;
2627 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2628 // to be one that progresses to the next set of op-codes, we need to advance to the last
2629 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2630 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2631 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2632 // the Date/Time op-code.
2634 SavedListEntry
= NewPos
;
2635 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2637 if (NewPos
->ForwardLink
!= &Menu
) {
2638 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2640 NewPos
= NewPos
->ForwardLink
;
2641 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2643 DistanceValue
+= NextMenuOption
->Skip
;
2644 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2646 // An option might be multi-line, so we need to reflect that data in the overall skip value
2648 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2650 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2651 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2652 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2653 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2659 // If we are going to scroll, update TopOfScreen
2661 if (Temp
> BottomRow
) {
2664 // Is the current top of screen a zero-advance op-code?
2665 // If so, keep moving forward till we hit a >0 advance op-code
2667 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2670 // If bottom op-code is more than one line or top op-code is more than one line
2672 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2674 // Is the bottom op-code greater than or equal in size to the top op-code?
2676 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2678 // Skip the top op-code
2680 TopOfScreen
= TopOfScreen
->ForwardLink
;
2681 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2683 OldSkipValue
= Difference
;
2685 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2688 // If we have a remainder, skip that many more op-codes until we drain the remainder
2691 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2692 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
2695 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2697 TopOfScreen
= TopOfScreen
->ForwardLink
;
2698 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2699 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
2700 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
2703 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
2704 TopOfScreen
= TopOfScreen
->ForwardLink
;
2705 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2706 Difference
= SavedMenuOption
->Skip
- Difference
;
2712 // Since we will act on this op-code in the next routine, and increment the
2713 // SkipValue, set the skips to one less than what is required.
2715 SkipValue
= Difference
- 1;
2719 // Since we will act on this op-code in the next routine, and increment the
2720 // SkipValue, set the skips to one less than what is required.
2722 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
2725 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
2726 TopOfScreen
= TopOfScreen
->ForwardLink
;
2729 SkipValue
= OldSkipValue
;
2733 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2734 // Let's set a skip flag to smoothly scroll the top of the screen.
2736 if (SavedMenuOption
->Skip
> 1) {
2737 if (SavedMenuOption
== NextMenuOption
) {
2744 TopOfScreen
= TopOfScreen
->ForwardLink
;
2746 } while (SavedMenuOption
->Skip
== 0);
2749 OldSkipValue
= SkipValue
;
2752 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
2754 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2757 SavedMenuOption
= MenuOption
;
2758 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2759 if (!IsSelectable (MenuOption
)) {
2761 // If we are at the end of the list and sitting on a text op, we need to more forward
2763 ScreenOperation
= UiUp
;
2764 ControlFlag
= CfScreenOperation
;
2768 MenuOption
= SavedMenuOption
;
2770 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2772 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2777 ControlFlag
= CfCheckSelection
;
2782 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2784 if (!EFI_ERROR (Status
)) {
2785 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2786 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2789 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
2790 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2798 ControlFlag
= CfCheckSelection
;
2800 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
2802 if (!EFI_ERROR (Status
)) {
2803 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2806 // Show NV update flag on status bar
2808 gNvUpdateRequired
= TRUE
;
2812 case CfUiNoOperation
:
2813 ControlFlag
= CfCheckSelection
;
2817 UiFreeRefreshList ();
2819 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2820 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
2821 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2822 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");