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
[] = {
78 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
131 Set Buffer to Value for Size bytes.
133 @param Buffer Memory to set.
134 @param Size Number of bytes to set
135 @param Value Value of the set operation.
157 Initialize Menu option list.
169 InitializeListHead (&Menu
);
174 Initialize Menu option list.
186 InitializeListHead (&gMenuList
);
191 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
193 @param Selection Menu selection.
199 UiRemoveMenuListEntry (
200 IN OUT UI_MENU_SELECTION
*Selection
203 UI_MENU_LIST
*UiMenuList
;
205 if (!IsListEmpty (&gMenuList
)) {
206 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
208 Selection
->FormId
= UiMenuList
->FormId
;
209 Selection
->QuestionId
= UiMenuList
->QuestionId
;
210 RemoveEntryList (&UiMenuList
->MenuLink
);
211 gBS
->FreePool (UiMenuList
);
217 Free Menu option linked list.
229 UI_MENU_LIST
*UiMenuList
;
231 while (!IsListEmpty (&gMenuList
)) {
232 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
233 RemoveEntryList (&UiMenuList
->MenuLink
);
234 gBS
->FreePool (UiMenuList
);
240 Add one menu entry to the linked lst
242 @param Selection Menu selection.
249 IN UI_MENU_SELECTION
*Selection
252 UI_MENU_LIST
*UiMenuList
;
254 UiMenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
255 ASSERT (UiMenuList
!= NULL
);
257 UiMenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
258 UiMenuList
->FormId
= Selection
->FormId
;
259 UiMenuList
->QuestionId
= Selection
->QuestionId
;
261 InsertHeadList (&gMenuList
, &UiMenuList
->MenuLink
);
266 Free Menu option linked list.
278 UI_MENU_OPTION
*MenuOption
;
280 while (!IsListEmpty (&Menu
)) {
281 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
282 RemoveEntryList (&MenuOption
->Link
);
285 // We allocated space for this description when we did a GetToken, free it here
287 if (MenuOption
->Skip
!= 0) {
289 // For date/time, MenuOption->Description is shared by three Menu Options
290 // Data format : [01/02/2004] [11:22:33]
291 // Line number : 0 0 1 0 0 1
293 gBS
->FreePool (MenuOption
->Description
);
295 gBS
->FreePool (MenuOption
);
301 Free Menu option linked list.
313 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
315 while (gMenuRefreshHead
!= NULL
) {
316 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
317 gBS
->FreePool (gMenuRefreshHead
);
318 gMenuRefreshHead
= OldMenuRefreshEntry
;
321 gMenuRefreshHead
= NULL
;
339 CHAR16
*OptionString
;
340 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
344 UI_MENU_SELECTION
*Selection
;
345 FORM_BROWSER_STATEMENT
*Question
;
349 if (gMenuRefreshHead
!= NULL
) {
351 MenuRefreshEntry
= gMenuRefreshHead
;
354 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
356 Selection
= MenuRefreshEntry
->Selection
;
357 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
360 // Don't update Question being edited
362 if (Question
!= MenuRefreshEntry
->Selection
->Statement
) {
364 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
365 if (EFI_ERROR (Status
)) {
369 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
371 if (OptionString
!= NULL
) {
373 // If leading spaces on OptionString - remove the spaces
375 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
378 for (Loop
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
379 OptionString
[Loop
] = OptionString
[Index
];
383 OptionString
[Loop
] = CHAR_NULL
;
385 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
386 gBS
->FreePool (OptionString
);
390 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
392 } while (MenuRefreshEntry
!= NULL
);
398 Wait for a given event to fire, or for an optional timeout to expire.
400 @param Event The event to wait for
401 @param Timeout An optional timeout value in 100 ns units.
402 @param RefreshInterval Menu refresh interval (in seconds).
404 @retval EFI_SUCCESS Event fired before Timeout expired.
405 @retval EFI_TIME_OUT Timout expired before Event fired.
409 UiWaitForSingleEvent (
411 IN UINT64 Timeout
, OPTIONAL
412 IN UINT8 RefreshInterval OPTIONAL
417 EFI_EVENT TimerEvent
;
418 EFI_EVENT WaitList
[2];
422 // Create a timer event
424 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
425 if (!EFI_ERROR (Status
)) {
427 // Set the timer event
436 // Wait for the original event or the timer
439 WaitList
[1] = TimerEvent
;
440 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
441 gBS
->CloseEvent (TimerEvent
);
444 // If the timer expired, change the return to timed out
446 if (!EFI_ERROR (Status
) && Index
== 1) {
447 Status
= EFI_TIMEOUT
;
452 // Update screen every second
454 if (RefreshInterval
== 0) {
455 Timeout
= ONE_SECOND
;
457 Timeout
= RefreshInterval
* ONE_SECOND
;
461 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
464 // Set the timer event
473 // Wait for the original event or the timer
476 WaitList
[1] = TimerEvent
;
477 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
480 // If the timer expired, update anything that needs a refresh and keep waiting
482 if (!EFI_ERROR (Status
) && Index
== 1) {
483 Status
= EFI_TIMEOUT
;
484 if (RefreshInterval
!= 0) {
489 gBS
->CloseEvent (TimerEvent
);
490 } while (Status
== EFI_TIMEOUT
);
498 Add one menu option by specified description and context.
500 @param String String description for this option.
501 @param Handle Hii handle for the package list.
502 @param Statement Statement of this Menu Option.
503 @param NumberOfLines Display lines for this Menu Option.
504 @param MenuItemCount The index for this Option in the Menu.
512 IN EFI_HII_HANDLE Handle
,
513 IN FORM_BROWSER_STATEMENT
*Statement
,
514 IN UINT16 NumberOfLines
,
515 IN UINT16 MenuItemCount
518 UI_MENU_OPTION
*MenuOption
;
524 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
526 // Add three MenuOptions for Date/Time
527 // Data format : [01/02/2004] [11:22:33]
528 // Line number : 0 0 1 0 0 1
533 if (Statement
->Storage
== NULL
) {
535 // For RTC type of date/time, set default refresh interval to be 1 second
537 if (Statement
->RefreshInterval
== 0) {
538 Statement
->RefreshInterval
= 1;
543 for (Index
= 0; Index
< Count
; Index
++) {
544 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
547 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
548 MenuOption
->Description
= String
;
549 MenuOption
->Handle
= Handle
;
550 MenuOption
->ThisTag
= Statement
;
551 MenuOption
->EntryNumber
= MenuItemCount
;
555 // Override LineNumber for the MenuOption in Date/Time sequence
557 MenuOption
->Skip
= 1;
559 MenuOption
->Skip
= NumberOfLines
;
561 MenuOption
->Sequence
= Index
;
563 if (Statement
->GrayOutExpression
!= NULL
) {
564 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
567 if ((Statement
->ValueExpression
!= NULL
) ||
568 (Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
)) {
569 MenuOption
->ReadOnly
= TRUE
;
572 InsertTailList (&Menu
, &MenuOption
->Link
);
578 Routine used to abstract a generic dialog interface and return the selected key or string
580 @param NumberOfLines The number of lines for the dialog box
581 @param HotKey Defines whether a single character is parsed
582 (TRUE) and returned in KeyValue or a string is
583 returned in StringBuffer. Two special characters
584 are considered when entering a string, a SCAN_ESC
585 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
586 string input and returns
587 @param MaximumStringSize The maximum size in bytes of a typed in string
588 (each character is a CHAR16) and the minimum
589 string returned is two bytes
590 @param StringBuffer The passed in pointer to the buffer which will
591 hold the typed in string if HotKey is FALSE
592 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
593 @param String Pointer to the first string in the list
594 @param ... A series of (quantity == NumberOfLines) text
595 strings which will be used to construct the dialog
598 @retval EFI_SUCCESS Displayed dialog and received user interaction
599 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
600 (StringBuffer == NULL) && (HotKey == FALSE))
601 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
606 IN UINTN NumberOfLines
,
608 IN UINTN MaximumStringSize
,
609 OUT CHAR16
*StringBuffer
,
610 OUT EFI_INPUT_KEY
*KeyValue
,
620 CHAR16
*BufferedString
;
627 BOOLEAN SelectionComplete
;
629 UINTN CurrentAttribute
;
630 UINTN DimensionsWidth
;
631 UINTN DimensionsHeight
;
633 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
634 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
636 SelectionComplete
= FALSE
;
638 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
639 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
640 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
643 ASSERT (BufferedString
);
645 VA_START (Marker
, String
);
648 // Zero the outgoing buffer
650 ZeroMem (StringBuffer
, MaximumStringSize
);
653 if (KeyValue
== NULL
) {
654 return EFI_INVALID_PARAMETER
;
657 if (StringBuffer
== NULL
) {
658 return EFI_INVALID_PARAMETER
;
664 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
666 LargestString
= (GetStringWidth (String
) / 2);
668 if (*String
== L
' ') {
672 // Determine the largest string in the dialog box
673 // Notice we are starting with 1 since String is the first string
675 for (Count
= 1; Count
< NumberOfLines
; Count
++) {
676 StackString
= VA_ARG (Marker
, CHAR16
*);
678 if (StackString
[0] == L
' ') {
679 InputOffset
= Count
+ 1;
682 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
684 // Size of the string visually and subtract the width by one for the null-terminator
686 LargestString
= (GetStringWidth (StackString
) / 2);
690 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
691 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
698 CreateSharedPopUp (LargestString
, NumberOfLines
, &String
);
701 // Take the first key typed and report it back?
704 Status
= WaitForKeyStroke (&Key
);
705 ASSERT_EFI_ERROR (Status
);
706 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
710 Status
= WaitForKeyStroke (&Key
);
712 switch (Key
.UnicodeChar
) {
714 switch (Key
.ScanCode
) {
716 gBS
->FreePool (TempString
);
717 gBS
->FreePool (BufferedString
);
718 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
719 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
720 return EFI_DEVICE_ERROR
;
728 case CHAR_CARRIAGE_RETURN
:
729 SelectionComplete
= TRUE
;
730 gBS
->FreePool (TempString
);
731 gBS
->FreePool (BufferedString
);
732 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
733 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
738 if (StringBuffer
[0] != CHAR_NULL
) {
739 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
740 TempString
[Index
] = StringBuffer
[Index
];
743 // Effectively truncate string by 1 character
745 TempString
[Index
- 1] = CHAR_NULL
;
746 StrCpy (StringBuffer
, TempString
);
751 // If it is the beginning of the string, don't worry about checking maximum limits
753 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
754 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
755 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
756 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
757 KeyPad
[0] = Key
.UnicodeChar
;
758 KeyPad
[1] = CHAR_NULL
;
759 StrCat (StringBuffer
, KeyPad
);
760 StrCat (TempString
, KeyPad
);
763 // If the width of the input string is now larger than the screen, we nee to
764 // adjust the index to start printing portions of the string
766 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
768 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
770 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
771 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
776 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
777 BufferedString
[Count
] = StringBuffer
[Index
];
780 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
783 } while (!SelectionComplete
);
786 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
787 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
793 IN UINTN RequestedWidth
,
794 IN UINTN NumberOfLines
,
795 IN CHAR16
**ArrayOfStrings
806 UINTN DimensionsWidth
;
807 UINTN DimensionsHeight
;
809 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
810 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
814 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
816 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
817 RequestedWidth
= DimensionsWidth
- 2;
821 // Subtract the PopUp width from total Columns, allow for one space extra on
822 // each end plus a border.
824 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
825 End
= Start
+ RequestedWidth
+ 1;
827 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
828 Bottom
= Top
+ NumberOfLines
+ 2;
830 Character
= BOXDRAW_DOWN_RIGHT
;
831 PrintCharAt (Start
, Top
, Character
);
832 Character
= BOXDRAW_HORIZONTAL
;
833 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
834 PrintChar (Character
);
837 Character
= BOXDRAW_DOWN_LEFT
;
838 PrintChar (Character
);
839 Character
= BOXDRAW_VERTICAL
;
840 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++) {
841 String
= ArrayOfStrings
[Count
];
845 // This will clear the background of the line - we never know who might have been
846 // here before us. This differs from the next clear in that it used the non-reverse
847 // video for normal printing.
849 if (GetStringWidth (String
) / 2 > 1) {
850 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
854 // Passing in a space results in the assumption that this is where typing will occur
856 if (String
[0] == L
' ') {
857 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
861 // Passing in a NULL results in a blank space
863 if (String
[0] == CHAR_NULL
) {
864 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
868 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
872 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
873 PrintCharAt (Start
, Index
+ 1, Character
);
874 PrintCharAt (End
- 1, Index
+ 1, Character
);
877 Character
= BOXDRAW_UP_RIGHT
;
878 PrintCharAt (Start
, Bottom
- 1, Character
);
879 Character
= BOXDRAW_HORIZONTAL
;
880 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
881 PrintChar (Character
);
884 Character
= BOXDRAW_UP_LEFT
;
885 PrintChar (Character
);
890 IN UINTN RequestedWidth
,
891 IN UINTN NumberOfLines
,
892 IN CHAR16
*ArrayOfStrings
,
896 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, &ArrayOfStrings
);
901 Update status bar on the bottom of menu.
903 @param MessageType The type of message to be shown.
904 @param Flags The flags in Question header.
905 @param State Set or clear.
912 IN UINTN MessageType
,
918 STATIC BOOLEAN InputError
;
919 CHAR16
*NvUpdateMessage
;
920 CHAR16
*InputErrorMessage
;
922 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
923 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
925 switch (MessageType
) {
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
930 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
931 gScreenDimensions
.BottomRow
- 1,
936 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
937 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
938 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
945 case NV_UPDATE_REQUIRED
:
946 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
948 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
950 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
951 gScreenDimensions
.BottomRow
- 1,
954 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
956 gNvUpdateRequired
= TRUE
;
958 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
959 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
961 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
962 gScreenDimensions
.BottomRow
- 1,
967 gNvUpdateRequired
= FALSE
;
972 case REFRESH_STATUS_BAR
:
974 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
977 if (gNvUpdateRequired
) {
978 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
986 gBS
->FreePool (InputErrorMessage
);
987 gBS
->FreePool (NvUpdateMessage
);
993 Get the supported width for a particular op-code
995 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
996 @param Handle The handle in the HII database being used
998 @return Returns the number of CHAR16 characters that is support.
1003 IN FORM_BROWSER_STATEMENT
*Statement
,
1004 IN EFI_HII_HANDLE Handle
1014 // See if the second text parameter is really NULL
1016 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1017 String
= GetToken (Statement
->TextTwo
, Handle
);
1018 Size
= StrLen (String
);
1019 gBS
->FreePool (String
);
1022 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1023 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1024 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1025 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1026 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1028 // Allow a wide display if text op-code and no secondary text op-code
1030 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1032 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1034 Width
= (UINT16
) gPromptBlockWidth
;
1037 if (Statement
->InSubtitle
) {
1038 Width
-= SUBTITLE_INDENT
;
1046 Will copy LineWidth amount of a string in the OutputString buffer and return the
1047 number of CHAR16 characters that were copied into the OutputString buffer.
1049 @param InputString String description for this option.
1050 @param LineWidth Width of the desired string to extract in CHAR16
1052 @param Index Where in InputString to start the copy process
1053 @param OutputString Buffer to copy the string into
1055 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1060 IN CHAR16
*InputString
,
1061 IN UINT16 LineWidth
,
1062 IN OUT UINTN
*Index
,
1063 OUT CHAR16
**OutputString
1066 static BOOLEAN Finished
;
1078 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1081 // Ensure we have got a valid buffer
1083 if (*OutputString
!= NULL
) {
1086 //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.
1087 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1089 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1090 *Index
= *Index
+ 2;
1094 // Fast-forward the string and see if there is a carriage-return in the string
1096 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1100 // Copy the desired LineWidth of data to the output buffer.
1101 // Also make sure that we don't copy more than the string.
1102 // Also make sure that if there are linefeeds, we account for them.
1104 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1105 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1108 // Convert to CHAR16 value and show that we are done with this operation
1110 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1111 if (LineWidth
!= 0) {
1115 if (Count2
== LineWidth
) {
1117 // Rewind the string from the maximum size until we see a space to break the line
1119 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1121 if (LineWidth
== 0) {
1129 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1132 // If currently pointing to a space, increment the index to the first non-space character
1135 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1139 *Index
= (UINT16
) (*Index
+ LineWidth
);
1148 Update display lines for a Menu Option.
1150 @param MenuOption The MenuOption to be checked.
1152 @retval TRUE This Menu Option is selectable.
1153 @retval FALSE This Menu Option could not be selected.
1157 UpdateOptionSkipLines (
1158 IN UI_MENU_SELECTION
*Selection
,
1159 IN UI_MENU_OPTION
*MenuOption
,
1160 IN CHAR16
**OptionalString
,
1168 CHAR16
*OutputString
;
1169 CHAR16
*OptionString
;
1172 OptionString
= *OptionalString
;
1173 OutputString
= NULL
;
1175 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1177 if (OptionString
!= NULL
) {
1178 Width
= (UINT16
) gOptionBlockWidth
;
1182 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1184 // If there is more string to process print on the next row and increment the Skip value
1186 if (StrLen (&OptionString
[Index
])) {
1187 if (SkipValue
== 0) {
1190 // Since the Number of lines for this menu entry may or may not be reflected accurately
1191 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1192 // some testing to ensure we are keeping this in-sync.
1194 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1196 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1202 gBS
->FreePool (OutputString
);
1203 if (SkipValue
!= 0) {
1211 *OptionalString
= OptionString
;
1216 Check whether this Menu Option could be highlighted.
1218 @param MenuOption The MenuOption to be checked.
1220 @retval TRUE This Menu Option is selectable.
1221 @retval FALSE This Menu Option could not be selected.
1227 UI_MENU_OPTION
*MenuOption
1230 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1231 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1240 Determine if the menu is the last menu that can be selected.
1242 @param Direction the scroll direction. False is down. True is up.
1244 @return FALSE -- the menu isn't the last menu that can be selected.
1245 @return TRUE -- the menu is the last menu that can be selected.
1251 IN BOOLEAN Direction
,
1252 IN LIST_ENTRY
*CurrentPos
1256 UI_MENU_OPTION
*MenuOption
;
1258 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1260 if (Temp
== &Menu
) {
1264 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1265 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1266 if (IsSelectable (MenuOption
)) {
1276 Move to next selectable statement.
1278 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1279 @param CurrentPosition Current position.
1281 @return The row distance from current MenuOption to next selectable MenuOption.
1286 MoveToNextStatement (
1288 IN OUT LIST_ENTRY
**CurrentPosition
1294 UI_MENU_OPTION
*NextMenuOption
;
1297 Pos
= *CurrentPosition
;
1301 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1302 if (IsSelectable (NextMenuOption
)) {
1305 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1309 Distance
+= NextMenuOption
->Skip
;
1310 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1315 // If we hit end there is still no statement can be focused,
1316 // we go backwards to find the statement can be focused.
1319 Pos
= *CurrentPosition
;
1322 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1323 if (IsSelectable (NextMenuOption
)) {
1326 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1330 Distance
-= NextMenuOption
->Skip
;
1331 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1335 *CurrentPosition
= &NextMenuOption
->Link
;
1341 Adjust Data and Time position accordingly.
1342 Data format : [01/02/2004] [11:22:33]
1343 Line number : 0 0 1 0 0 1
1345 @param DirectionUp the up or down direction. False is down. True is
1347 @param CurrentPosition Current position. On return: Point to the last
1348 Option (Year or Second) if up; Point to the first
1349 Option (Month or Hour) if down.
1351 @return Return line number to pad. It is possible that we stand on a zero-advance
1352 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1357 AdjustDateAndTimePosition (
1358 IN BOOLEAN DirectionUp
,
1359 IN OUT LIST_ENTRY
**CurrentPosition
1363 LIST_ENTRY
*NewPosition
;
1364 UI_MENU_OPTION
*MenuOption
;
1365 UINTN PadLineNumber
;
1368 NewPosition
= *CurrentPosition
;
1369 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1371 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1372 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1374 // Calculate the distance from current position to the last Date/Time MenuOption
1377 while (MenuOption
->Skip
== 0) {
1379 NewPosition
= NewPosition
->ForwardLink
;
1380 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1384 NewPosition
= *CurrentPosition
;
1387 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1388 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1389 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1390 // checking can be done.
1392 while (Count
++ < 2) {
1393 NewPosition
= NewPosition
->BackLink
;
1397 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1398 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1399 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1400 // checking can be done.
1402 while (Count
-- > 0) {
1403 NewPosition
= NewPosition
->ForwardLink
;
1407 *CurrentPosition
= NewPosition
;
1410 return PadLineNumber
;
1415 Display menu and wait for user to select one menu option, then return it.
1416 If AutoBoot is enabled, then if user doesn't select any option,
1417 after period of time, it will automatically return the first menu option.
1420 @return Return the pointer of the menu which selected,
1421 @return otherwise return NULL.
1426 IN OUT UI_MENU_SELECTION
*Selection
1432 UINTN DistanceValue
;
1444 CHAR16
*OptionString
;
1445 CHAR16
*OutputString
;
1446 CHAR16
*FormattedString
;
1456 LIST_ENTRY
*TopOfScreen
;
1457 LIST_ENTRY
*SavedListEntry
;
1458 UI_MENU_OPTION
*MenuOption
;
1459 UI_MENU_OPTION
*NextMenuOption
;
1460 UI_MENU_OPTION
*SavedMenuOption
;
1461 UI_MENU_OPTION
*PreviousMenuOption
;
1462 UI_CONTROL_FLAG ControlFlag
;
1463 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1464 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1465 UI_SCREEN_OPERATION ScreenOperation
;
1466 UINT8 MinRefreshInterval
;
1469 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1470 FORM_BROWSER_STATEMENT
*Statement
;
1472 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1474 Status
= EFI_SUCCESS
;
1475 FormattedString
= NULL
;
1476 OptionString
= NULL
;
1477 ScreenOperation
= UiNoOperation
;
1479 MinRefreshInterval
= 0;
1482 OutputString
= NULL
;
1487 MenuRefreshEntry
= gMenuRefreshHead
;
1489 NextMenuOption
= NULL
;
1490 PreviousMenuOption
= NULL
;
1491 SavedMenuOption
= NULL
;
1493 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1495 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
1496 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1497 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1499 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1500 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1503 Col
= LocalScreen
.LeftColumn
;
1504 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1506 Selection
->TopRow
= TopRow
;
1507 Selection
->BottomRow
= BottomRow
;
1508 Selection
->PromptCol
= Col
;
1509 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1510 Selection
->Statement
= NULL
;
1512 TopOfScreen
= Menu
.ForwardLink
;
1517 // Get user's selection
1519 NewPos
= Menu
.ForwardLink
;
1521 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1522 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1524 ControlFlag
= CfInitialization
;
1525 Selection
->Action
= UI_ACTION_NONE
;
1527 switch (ControlFlag
) {
1528 case CfInitialization
:
1529 if (IsListEmpty (&Menu
)) {
1530 ControlFlag
= CfReadKey
;
1532 ControlFlag
= CfCheckSelection
;
1536 case CfCheckSelection
:
1537 if (Selection
->Action
!= UI_ACTION_NONE
) {
1538 ControlFlag
= CfExit
;
1540 ControlFlag
= CfRepaint
;
1545 ControlFlag
= CfRefreshHighLight
;
1559 LocalScreen
.LeftColumn
,
1560 LocalScreen
.RightColumn
,
1561 TopRow
- SCROLL_ARROW_HEIGHT
,
1562 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1563 FIELD_TEXT
| FIELD_BACKGROUND
1566 UiFreeRefreshList ();
1567 MinRefreshInterval
= 0;
1569 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1570 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1571 MenuOption
->Row
= Row
;
1572 MenuOption
->Col
= Col
;
1573 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1575 Statement
= MenuOption
->ThisTag
;
1576 if (Statement
->InSubtitle
) {
1577 MenuOption
->Col
+= SUBTITLE_INDENT
;
1580 if (MenuOption
->GrayOut
) {
1581 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1583 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1584 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1588 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1591 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1592 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1593 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1596 // If there is more string to process print on the next row and increment the Skip value
1598 if (StrLen (&MenuOption
->Description
[Index
])) {
1604 gBS
->FreePool (OutputString
);
1613 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1614 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1616 if (OptionString
!= NULL
) {
1617 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1619 // If leading spaces on OptionString - remove the spaces
1621 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1622 MenuOption
->OptCol
++;
1625 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1626 OptionString
[Count
] = OptionString
[Index
];
1630 OptionString
[Count
] = CHAR_NULL
;
1634 // If Question request refresh, register the op-code
1636 if (Statement
->RefreshInterval
!= 0) {
1638 // Menu will be refreshed at minimal interval of all Questions
1639 // which have refresh request
1641 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1642 MinRefreshInterval
= Statement
->RefreshInterval
;
1645 if (gMenuRefreshHead
== NULL
) {
1646 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1647 ASSERT (MenuRefreshEntry
!= NULL
);
1648 MenuRefreshEntry
->MenuOption
= MenuOption
;
1649 MenuRefreshEntry
->Selection
= Selection
;
1650 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1651 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1652 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1653 gMenuRefreshHead
= MenuRefreshEntry
;
1656 // Advance to the last entry
1658 for (MenuRefreshEntry
= gMenuRefreshHead
;
1659 MenuRefreshEntry
->Next
!= NULL
;
1660 MenuRefreshEntry
= MenuRefreshEntry
->Next
1663 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1664 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1665 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1666 MenuRefreshEntry
->MenuOption
= MenuOption
;
1667 MenuRefreshEntry
->Selection
= Selection
;
1668 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1669 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1670 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1674 Width
= (UINT16
) gOptionBlockWidth
;
1677 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1678 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1679 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1682 // If there is more string to process print on the next row and increment the Skip value
1684 if (StrLen (&OptionString
[Index
])) {
1688 // Since the Number of lines for this menu entry may or may not be reflected accurately
1689 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1690 // some testing to ensure we are keeping this in-sync.
1692 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1694 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1700 gBS
->FreePool (OutputString
);
1709 gBS
->FreePool (OptionString
);
1712 // If this is a text op with secondary text information
1714 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1715 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1717 Width
= (UINT16
) gOptionBlockWidth
;
1720 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1721 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1722 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1725 // If there is more string to process print on the next row and increment the Skip value
1727 if (StrLen (&StringPtr
[Index
])) {
1731 // Since the Number of lines for this menu entry may or may not be reflected accurately
1732 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1733 // some testing to ensure we are keeping this in-sync.
1735 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1737 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1743 gBS
->FreePool (OutputString
);
1750 gBS
->FreePool (StringPtr
);
1754 // Need to handle the bottom of the display
1756 if (MenuOption
->Skip
> 1) {
1757 Row
+= MenuOption
->Skip
- SkipValue
;
1760 Row
+= MenuOption
->Skip
;
1763 if (Row
> BottomRow
) {
1764 if (!ValueIsScroll (FALSE
, Link
)) {
1768 Row
= BottomRow
+ 1;
1773 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
1778 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1780 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1781 TopRow
- SCROLL_ARROW_HEIGHT
,
1785 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1789 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1791 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1792 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1796 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1803 case CfRefreshHighLight
:
1805 // MenuOption: Last menu option that need to remove hilight
1806 // MenuOption is set to NULL in Repaint
1807 // NewPos: Current menu option that need to hilight
1809 ControlFlag
= CfUpdateHelpString
;
1812 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1813 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1815 SavedValue
= Repaint
;
1818 if (Selection
->QuestionId
!= 0) {
1819 NewPos
= Menu
.ForwardLink
;
1820 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1822 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
1823 NewPos
= NewPos
->ForwardLink
;
1824 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1826 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
1828 // Target Question found, find its MenuOption
1832 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
1833 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1834 Index
+= SavedMenuOption
->Skip
;
1835 Link
= Link
->ForwardLink
;
1838 if (Link
!= NewPos
|| Index
> BottomRow
) {
1840 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1843 for (Index
= TopRow
; Index
<= BottomRow
; ) {
1844 Link
= Link
->BackLink
;
1845 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1846 Index
+= SavedMenuOption
->Skip
;
1848 TopOfScreen
= Link
->ForwardLink
;
1852 ControlFlag
= CfRepaint
;
1857 // Target Question not found, highlight the default menu option
1859 NewPos
= TopOfScreen
;
1862 Selection
->QuestionId
= 0;
1865 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
1866 if (MenuOption
!= NULL
) {
1868 // Remove highlight on last Menu Option
1870 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1871 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1872 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1873 if (OptionString
!= NULL
) {
1874 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1875 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
1878 // If leading spaces on OptionString - remove the spaces
1880 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1883 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1884 OptionString
[Count
] = OptionString
[Index
];
1888 OptionString
[Count
] = CHAR_NULL
;
1891 Width
= (UINT16
) gOptionBlockWidth
;
1892 OriginalRow
= MenuOption
->Row
;
1894 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1895 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1896 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1899 // If there is more string to process print on the next row and increment the Skip value
1901 if (StrLen (&OptionString
[Index
])) {
1905 gBS
->FreePool (OutputString
);
1908 MenuOption
->Row
= OriginalRow
;
1910 gBS
->FreePool (OptionString
);
1913 if (MenuOption
->GrayOut
) {
1914 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1915 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1916 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1919 OriginalRow
= MenuOption
->Row
;
1920 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
1922 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1923 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1924 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1927 // If there is more string to process print on the next row and increment the Skip value
1929 if (StrLen (&MenuOption
->Description
[Index
])) {
1933 gBS
->FreePool (OutputString
);
1936 MenuOption
->Row
= OriginalRow
;
1937 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1943 // This is only possible if we entered this page and the first menu option is
1944 // a "non-menu" item. In that case, force it UiDown
1946 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1947 if (!IsSelectable (MenuOption
)) {
1948 ASSERT (ScreenOperation
== UiNoOperation
);
1949 ScreenOperation
= UiDown
;
1950 ControlFlag
= CfScreenOperation
;
1955 // This is the current selected statement
1957 Statement
= MenuOption
->ThisTag
;
1958 Selection
->Statement
= Statement
;
1961 // Set reverse attribute
1963 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
1964 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1967 // Assuming that we have a refresh linked-list created, lets annotate the
1968 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1969 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1971 if (gMenuRefreshHead
!= NULL
) {
1972 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
1973 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1974 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
1975 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
1980 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1981 if (OptionString
!= NULL
) {
1982 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1984 // If leading spaces on OptionString - remove the spaces
1986 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1989 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1990 OptionString
[Count
] = OptionString
[Index
];
1994 OptionString
[Count
] = CHAR_NULL
;
1996 Width
= (UINT16
) gOptionBlockWidth
;
1998 OriginalRow
= MenuOption
->Row
;
2000 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2001 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2002 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2005 // If there is more string to process print on the next row and increment the Skip value
2007 if (StrLen (&OptionString
[Index
])) {
2011 gBS
->FreePool (OutputString
);
2014 MenuOption
->Row
= OriginalRow
;
2016 gBS
->FreePool (OptionString
);
2019 OriginalRow
= MenuOption
->Row
;
2021 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2023 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2024 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2025 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2028 // If there is more string to process print on the next row and increment the Skip value
2030 if (StrLen (&MenuOption
->Description
[Index
])) {
2034 gBS
->FreePool (OutputString
);
2037 MenuOption
->Row
= OriginalRow
;
2042 if (((NewPos
->ForwardLink
!= &Menu
) && (ScreenOperation
== UiDown
)) ||
2043 ((NewPos
->BackLink
!= &Menu
) && (ScreenOperation
== UiUp
)) ||
2044 (ScreenOperation
== UiNoOperation
)
2046 UpdateKeyHelp (MenuOption
, FALSE
);
2049 // Clear reverse attribute
2051 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2054 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2055 // if we didn't break halfway when process CfRefreshHighLight.
2057 Repaint
= SavedValue
;
2060 case CfUpdateHelpString
:
2061 ControlFlag
= CfPrepareToReadKey
;
2063 if ((Repaint
|| NewLine
) && (gClassOfVfr
!= EFI_GENERAL_APPLICATION_SUBCLASS
)) {
2065 // Don't print anything if it is a NULL help token
2067 if (MenuOption
->ThisTag
->Help
== 0) {
2070 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2073 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2075 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2077 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2079 // Pad String with spaces to simulate a clearing of the previous line
2081 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2082 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2086 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2088 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2093 // Reset this flag every time we finish using it.
2099 case CfPrepareToReadKey
:
2100 ControlFlag
= CfReadKey
;
2101 ScreenOperation
= UiNoOperation
;
2105 ControlFlag
= CfScreenOperation
;
2108 // Wait for user's selection
2111 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2112 } while (Status
== EFI_TIMEOUT
);
2114 if (Status
== EFI_TIMEOUT
) {
2115 Key
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
2117 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2119 // if we encounter error, continue to read another key in.
2121 if (EFI_ERROR (Status
)) {
2122 ControlFlag
= CfReadKey
;
2127 if (IsListEmpty (&Menu
) && Key
.UnicodeChar
!= CHAR_NULL
) {
2129 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2134 switch (Key
.UnicodeChar
) {
2135 case CHAR_CARRIAGE_RETURN
:
2136 ScreenOperation
= UiSelect
;
2141 // We will push the adjustment of these numeric values directly to the input handler
2142 // NOTE: we won't handle manual input numeric
2146 Statement
= MenuOption
->ThisTag
;
2147 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2148 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2149 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2151 if (Key
.UnicodeChar
== '+') {
2152 gDirection
= SCAN_RIGHT
;
2154 gDirection
= SCAN_LEFT
;
2156 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2157 SafeFreePool (OptionString
);
2162 ScreenOperation
= UiUp
;
2167 ScreenOperation
= UiDown
;
2171 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
2172 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2173 ScreenOperation
= UiSelect
;
2179 if (((Key
.ScanCode
== SCAN_F1
) && ((gFunctionKeySetting
& FUNCTION_ONE
) != FUNCTION_ONE
)) ||
2180 ((Key
.ScanCode
== SCAN_F2
) && ((gFunctionKeySetting
& FUNCTION_TWO
) != FUNCTION_TWO
)) ||
2181 ((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2182 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2185 // If the function key has been disabled, just ignore the key.
2188 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2189 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2190 if (Key
.ScanCode
== SCAN_F9
) {
2192 // Reset to standard default
2194 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2196 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2205 case CfScreenOperation
:
2206 if (ScreenOperation
!= UiPrevious
&& ScreenOperation
!= UiReset
) {
2208 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2209 // ignore the selection and go back to reading keys.
2211 if (IsListEmpty (&Menu
)) {
2212 ControlFlag
= CfReadKey
;
2216 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2218 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2219 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2220 if (IsSelectable (NextMenuOption
)) {
2225 if (Link
== &Menu
) {
2226 ControlFlag
= CfPrepareToReadKey
;
2229 } else if (ScreenOperation
== UiReset
) {
2231 // Press ESC to exit FormSet
2233 Selection
->Action
= UI_ACTION_EXIT
;
2234 Selection
->Statement
= NULL
;
2238 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2241 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2242 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2249 ControlFlag
= CfCheckSelection
;
2251 if (IsListEmpty (&gMenuList
)) {
2252 Selection
->Action
= UI_ACTION_NONE
;
2253 if (IsListEmpty (&Menu
)) {
2254 ControlFlag
= CfReadKey
;
2260 // Remove the Cached page entry
2262 UiRemoveMenuListEntry (Selection
);
2264 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2265 Selection
->Statement
= NULL
;
2269 ControlFlag
= CfCheckSelection
;
2271 Statement
= MenuOption
->ThisTag
;
2272 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2273 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2274 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2275 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2280 // Keep highlight on current MenuOption
2282 Selection
->QuestionId
= Statement
->QuestionId
;
2284 switch (Statement
->Operand
) {
2285 case EFI_IFR_REF_OP
:
2286 if (Statement
->RefDevicePath
!= 0) {
2288 // Goto another Hii Package list
2290 ControlFlag
= CfUiReset
;
2291 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2293 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2294 if (StringPtr
== NULL
) {
2296 // No device path string not found, exit
2298 Selection
->Action
= UI_ACTION_EXIT
;
2299 Selection
->Statement
= NULL
;
2302 BufferSize
= StrLen (StringPtr
) / 2;
2303 DevicePath
= AllocatePool (BufferSize
);
2305 HexStringToBuffer ((UINT8
*) DevicePath
, &BufferSize
, StringPtr
);
2306 Selection
->Handle
= HiiLibDevicePathToHiiHandle (DevicePath
);
2307 if (Selection
->Handle
== NULL
) {
2309 // If target Hii Handle not found, exit
2311 Selection
->Action
= UI_ACTION_EXIT
;
2312 Selection
->Statement
= NULL
;
2316 gBS
->FreePool (StringPtr
);
2317 gBS
->FreePool (DevicePath
);
2319 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2320 Selection
->FormId
= Statement
->RefFormId
;
2321 Selection
->QuestionId
= Statement
->RefQuestionId
;
2322 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2324 // Goto another Formset, check for uncommitted data
2326 ControlFlag
= CfUiReset
;
2327 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2329 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2330 Selection
->FormId
= Statement
->RefFormId
;
2331 Selection
->QuestionId
= Statement
->RefQuestionId
;
2332 } else if (Statement
->RefFormId
!= 0) {
2334 // Goto another form inside this formset,
2336 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2339 // Link current form so that we can always go back when someone hits the UiPrevious
2341 UiAddMenuListEntry (Selection
);
2343 Selection
->FormId
= Statement
->RefFormId
;
2344 Selection
->QuestionId
= Statement
->RefQuestionId
;
2345 } else if (Statement
->RefQuestionId
!= 0) {
2347 // Goto another Question
2349 Selection
->QuestionId
= Statement
->RefQuestionId
;
2351 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
)) {
2352 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2361 case EFI_IFR_ACTION_OP
:
2363 // Process the Config string <ConfigResp>
2365 Status
= ProcessQuestionConfig (Selection
, Statement
);
2367 if (EFI_ERROR (Status
)) {
2372 // The action button may change some Question value, so refresh the form
2374 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2377 case EFI_IFR_RESET_BUTTON_OP
:
2379 // Reset Question to default value specified by DefaultId
2381 ControlFlag
= CfUiDefault
;
2382 DefaultId
= Statement
->DefaultId
;
2387 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2389 UpdateKeyHelp (MenuOption
, TRUE
);
2390 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2392 if (EFI_ERROR (Status
)) {
2398 if (OptionString
!= NULL
) {
2399 PrintStringAt (LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ 1, MenuOption
->Row
, OptionString
);
2400 gBS
->FreePool (OptionString
);
2403 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2410 // We are going to leave current FormSet, so check uncommited data in this FormSet
2412 ControlFlag
= CfCheckSelection
;
2414 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
2416 // There is no parent menu for FrontPage
2418 Selection
->Action
= UI_ACTION_NONE
;
2419 Selection
->Statement
= MenuOption
->ThisTag
;
2424 // If NV flag is up, prompt user
2426 if (gNvUpdateRequired
) {
2427 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2429 YesResponse
= gYesResponse
[0];
2430 NoResponse
= gNoResponse
[0];
2433 CreateDialog (3, TRUE
, 0, NULL
, &Key
, gEmptyString
, gAreYouSure
, gEmptyString
);
2436 (Key
.ScanCode
!= SCAN_ESC
) &&
2437 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2438 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2442 // If the user hits the YesResponse key
2444 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2449 Selection
->Action
= UI_ACTION_NONE
;
2454 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2455 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2458 gST
->ConOut
->ClearScreen (gST
->ConOut
);
2462 ControlFlag
= CfCheckSelection
;
2463 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2464 if (MenuOption
->Sequence
!= 0) {
2466 // In the middle or tail of the Date/Time op-code set, go left.
2468 NewPos
= NewPos
->BackLink
;
2474 ControlFlag
= CfCheckSelection
;
2475 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2476 if (MenuOption
->Sequence
!= 2) {
2478 // In the middle or tail of the Date/Time op-code set, go left.
2480 NewPos
= NewPos
->ForwardLink
;
2486 ControlFlag
= CfCheckSelection
;
2488 SavedListEntry
= TopOfScreen
;
2490 if (NewPos
->BackLink
!= &Menu
) {
2493 // Adjust Date/Time position before we advance forward.
2495 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2498 // Caution that we have already rewind to the top, don't go backward in this situation.
2500 if (NewPos
->BackLink
!= &Menu
) {
2501 NewPos
= NewPos
->BackLink
;
2504 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2505 DistanceValue
= PreviousMenuOption
->Skip
;
2508 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2509 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2510 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2511 // checking can be done.
2513 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2516 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2517 // don't worry about a redraw.
2519 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2521 TopOfScreen
= NewPos
;
2524 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2525 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2526 if (Difference
> 0) {
2528 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2530 TopOfScreen
= NewPos
;
2534 if (Difference
< 0) {
2536 // We want to goto previous MenuOption, but finally we go down.
2537 // it means that we hit the begining MenuOption that can be focused
2538 // so we simply scroll to the top
2540 if (SavedListEntry
!= Menu
.ForwardLink
) {
2541 TopOfScreen
= Menu
.ForwardLink
;
2547 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2549 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2551 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2553 SavedMenuOption
= MenuOption
;
2554 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2555 if (!IsSelectable (MenuOption
)) {
2557 // If we are at the end of the list and sitting on a text op, we need to more forward
2559 ScreenOperation
= UiDown
;
2560 ControlFlag
= CfScreenOperation
;
2564 MenuOption
= SavedMenuOption
;
2569 ControlFlag
= CfCheckSelection
;
2571 if (NewPos
->BackLink
== &Menu
) {
2580 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2582 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2583 Index
= Index
- PreviousMenuOption
->Skip
;
2584 Link
= Link
->BackLink
;
2585 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2589 Difference
= MoveToNextStatement (TRUE
, &Link
);
2590 if (Difference
> 0) {
2592 // The focus MenuOption is above the TopOfScreen
2595 } else if (Difference
< 0) {
2597 // This happens when there is no MenuOption can be focused from
2598 // Current MenuOption to the first MenuOption
2600 TopOfScreen
= Menu
.ForwardLink
;
2602 Index
+= Difference
;
2603 if (Index
< TopRow
) {
2607 if (NewPos
== Link
) {
2615 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2616 // Don't do this when we are already in the first page.
2618 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2619 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2623 ControlFlag
= CfCheckSelection
;
2625 if (NewPos
->ForwardLink
== &Menu
) {
2634 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2636 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2637 Index
= Index
+ NextMenuOption
->Skip
;
2638 Link
= Link
->ForwardLink
;
2639 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2642 Index
+= MoveToNextStatement (FALSE
, &Link
);
2643 if (Index
> BottomRow
) {
2645 // There are more MenuOption needing scrolling
2650 if (NewPos
== Link
&& Index
<= BottomRow
) {
2652 // Finally we know that NewPos is the last MenuOption can be focused.
2661 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2662 // Don't do this when we are already in the last page.
2664 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2665 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2669 ControlFlag
= CfCheckSelection
;
2671 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2672 // to be one that progresses to the next set of op-codes, we need to advance to the last
2673 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2674 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2675 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2676 // the Date/Time op-code.
2678 SavedListEntry
= NewPos
;
2679 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2681 if (NewPos
->ForwardLink
!= &Menu
) {
2682 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2684 NewPos
= NewPos
->ForwardLink
;
2685 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2687 DistanceValue
+= NextMenuOption
->Skip
;
2688 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2690 // An option might be multi-line, so we need to reflect that data in the overall skip value
2692 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2694 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2695 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2696 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2697 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2703 // If we are going to scroll, update TopOfScreen
2705 if (Temp
> BottomRow
) {
2708 // Is the current top of screen a zero-advance op-code?
2709 // If so, keep moving forward till we hit a >0 advance op-code
2711 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2714 // If bottom op-code is more than one line or top op-code is more than one line
2716 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2718 // Is the bottom op-code greater than or equal in size to the top op-code?
2720 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2722 // Skip the top op-code
2724 TopOfScreen
= TopOfScreen
->ForwardLink
;
2725 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2727 OldSkipValue
= Difference
;
2729 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2732 // If we have a remainder, skip that many more op-codes until we drain the remainder
2735 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2736 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
2739 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2741 TopOfScreen
= TopOfScreen
->ForwardLink
;
2742 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2743 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
2744 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
2747 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
2748 TopOfScreen
= TopOfScreen
->ForwardLink
;
2749 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2750 Difference
= SavedMenuOption
->Skip
- Difference
;
2756 // Since we will act on this op-code in the next routine, and increment the
2757 // SkipValue, set the skips to one less than what is required.
2759 SkipValue
= Difference
- 1;
2763 // Since we will act on this op-code in the next routine, and increment the
2764 // SkipValue, set the skips to one less than what is required.
2766 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
2769 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
2770 TopOfScreen
= TopOfScreen
->ForwardLink
;
2773 SkipValue
= OldSkipValue
;
2777 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2778 // Let's set a skip flag to smoothly scroll the top of the screen.
2780 if (SavedMenuOption
->Skip
> 1) {
2781 if (SavedMenuOption
== NextMenuOption
) {
2788 TopOfScreen
= TopOfScreen
->ForwardLink
;
2790 } while (SavedMenuOption
->Skip
== 0);
2793 OldSkipValue
= SkipValue
;
2796 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
2798 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2801 SavedMenuOption
= MenuOption
;
2802 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2803 if (!IsSelectable (MenuOption
)) {
2805 // If we are at the end of the list and sitting on a text op, we need to more forward
2807 ScreenOperation
= UiUp
;
2808 ControlFlag
= CfScreenOperation
;
2812 MenuOption
= SavedMenuOption
;
2814 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2816 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2821 ControlFlag
= CfCheckSelection
;
2826 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2828 if (!EFI_ERROR (Status
)) {
2829 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2830 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2833 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
2834 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2842 ControlFlag
= CfCheckSelection
;
2844 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
2846 if (!EFI_ERROR (Status
)) {
2847 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2850 // Show NV update flag on status bar
2852 gNvUpdateRequired
= TRUE
;
2856 case CfUiNoOperation
:
2857 ControlFlag
= CfCheckSelection
;
2861 UiFreeRefreshList ();
2863 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2864 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
2865 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2866 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");