2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2007, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
20 MENU_REFRESH_ENTRY
*gMenuRefreshHead
;
23 // Search table for UiDisplayMenu()
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
68 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
121 Set Buffer to Value for Size bytes.
123 @param Buffer Memory to set.
124 @param Size Number of bytes to set
125 @param Value Value of the set operation.
140 while ((Size
--) != 0) {
147 Initialize Menu option list.
155 InitializeListHead (&Menu
);
160 Initialize Menu option list.
168 InitializeListHead (&gMenuList
);
173 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
175 @param Selection Menu selection.
179 UiRemoveMenuListEntry (
180 OUT UI_MENU_SELECTION
*Selection
183 UI_MENU_LIST
*UiMenuList
;
185 if (!IsListEmpty (&gMenuList
)) {
186 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
188 Selection
->FormId
= UiMenuList
->FormId
;
189 Selection
->QuestionId
= UiMenuList
->QuestionId
;
190 RemoveEntryList (&UiMenuList
->MenuLink
);
191 gBS
->FreePool (UiMenuList
);
197 Free Menu option linked list.
205 UI_MENU_LIST
*UiMenuList
;
207 while (!IsListEmpty (&gMenuList
)) {
208 UiMenuList
= CR (gMenuList
.ForwardLink
, UI_MENU_LIST
, MenuLink
, UI_MENU_LIST_SIGNATURE
);
209 RemoveEntryList (&UiMenuList
->MenuLink
);
210 gBS
->FreePool (UiMenuList
);
216 Add one menu entry to the linked lst
218 @param Selection Menu selection.
223 IN UI_MENU_SELECTION
*Selection
226 UI_MENU_LIST
*UiMenuList
;
228 UiMenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
229 ASSERT (UiMenuList
!= NULL
);
231 UiMenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
232 UiMenuList
->FormId
= Selection
->FormId
;
233 UiMenuList
->QuestionId
= Selection
->QuestionId
;
235 InsertHeadList (&gMenuList
, &UiMenuList
->MenuLink
);
240 Free Menu option linked list.
248 UI_MENU_OPTION
*MenuOption
;
250 while (!IsListEmpty (&Menu
)) {
251 MenuOption
= MENU_OPTION_FROM_LINK (Menu
.ForwardLink
);
252 RemoveEntryList (&MenuOption
->Link
);
255 // We allocated space for this description when we did a GetToken, free it here
257 if (MenuOption
->Skip
!= 0) {
259 // For date/time, MenuOption->Description is shared by three Menu Options
260 // Data format : [01/02/2004] [11:22:33]
261 // Line number : 0 0 1 0 0 1
263 gBS
->FreePool (MenuOption
->Description
);
265 gBS
->FreePool (MenuOption
);
271 Free Menu option linked list.
279 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
281 while (gMenuRefreshHead
!= NULL
) {
282 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
283 gBS
->FreePool (gMenuRefreshHead
);
284 gMenuRefreshHead
= OldMenuRefreshEntry
;
287 gMenuRefreshHead
= NULL
;
301 CHAR16
*OptionString
;
302 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
306 UI_MENU_SELECTION
*Selection
;
307 FORM_BROWSER_STATEMENT
*Question
;
311 if (gMenuRefreshHead
!= NULL
) {
313 MenuRefreshEntry
= gMenuRefreshHead
;
316 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
318 Selection
= MenuRefreshEntry
->Selection
;
319 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
322 // Don't update Question being edited
324 if (Question
!= MenuRefreshEntry
->Selection
->Statement
) {
326 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
327 if (EFI_ERROR (Status
)) {
331 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
333 if (OptionString
!= NULL
) {
335 // If leading spaces on OptionString - remove the spaces
337 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
340 for (Loop
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
341 OptionString
[Loop
] = OptionString
[Index
];
345 OptionString
[Loop
] = CHAR_NULL
;
347 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
348 gBS
->FreePool (OptionString
);
352 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
354 } while (MenuRefreshEntry
!= NULL
);
360 Wait for a given event to fire, or for an optional timeout to expire.
362 @param Event The event to wait for
363 @param Timeout An optional timeout value in 100 ns units.
364 @param RefreshInterval Menu refresh interval (in seconds).
366 @retval EFI_SUCCESS Event fired before Timeout expired.
367 @retval EFI_TIME_OUT Timout expired before Event fired.
371 UiWaitForSingleEvent (
373 IN UINT64 Timeout
, OPTIONAL
374 IN UINT8 RefreshInterval OPTIONAL
379 EFI_EVENT TimerEvent
;
380 EFI_EVENT WaitList
[2];
384 // Create a timer event
386 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
387 if (!EFI_ERROR (Status
)) {
389 // Set the timer event
398 // Wait for the original event or the timer
401 WaitList
[1] = TimerEvent
;
402 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
403 gBS
->CloseEvent (TimerEvent
);
406 // If the timer expired, change the return to timed out
408 if (!EFI_ERROR (Status
) && Index
== 1) {
409 Status
= EFI_TIMEOUT
;
414 // Update screen every second
416 if (RefreshInterval
== 0) {
417 Timeout
= ONE_SECOND
;
419 Timeout
= RefreshInterval
* ONE_SECOND
;
423 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
426 // Set the timer event
435 // Wait for the original event or the timer
438 WaitList
[1] = TimerEvent
;
439 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
442 // If the timer expired, update anything that needs a refresh and keep waiting
444 if (!EFI_ERROR (Status
) && Index
== 1) {
445 Status
= EFI_TIMEOUT
;
446 if (RefreshInterval
!= 0) {
451 gBS
->CloseEvent (TimerEvent
);
452 } while (Status
== EFI_TIMEOUT
);
460 Add one menu option by specified description and context.
462 @param String String description for this option.
463 @param Handle Hii handle for the package list.
464 @param Statement Statement of this Menu Option.
465 @param NumberOfLines Display lines for this Menu Option.
466 @param MenuItemCount The index for this Option in the Menu.
472 IN EFI_HII_HANDLE Handle
,
473 IN FORM_BROWSER_STATEMENT
*Statement
,
474 IN UINT16 NumberOfLines
,
475 IN UINT16 MenuItemCount
478 UI_MENU_OPTION
*MenuOption
;
484 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
486 // Add three MenuOptions for Date/Time
487 // Data format : [01/02/2004] [11:22:33]
488 // Line number : 0 0 1 0 0 1
493 if (Statement
->Storage
== NULL
) {
495 // For RTC type of date/time, set default refresh interval to be 1 second
497 if (Statement
->RefreshInterval
== 0) {
498 Statement
->RefreshInterval
= 1;
503 for (Index
= 0; Index
< Count
; Index
++) {
504 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
507 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
508 MenuOption
->Description
= String
;
509 MenuOption
->Handle
= Handle
;
510 MenuOption
->ThisTag
= Statement
;
511 MenuOption
->EntryNumber
= MenuItemCount
;
515 // Override LineNumber for the MenuOption in Date/Time sequence
517 MenuOption
->Skip
= 1;
519 MenuOption
->Skip
= NumberOfLines
;
521 MenuOption
->Sequence
= Index
;
523 if (Statement
->GrayOutExpression
!= NULL
) {
524 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
527 if ((Statement
->ValueExpression
!= NULL
) ||
528 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
529 MenuOption
->ReadOnly
= TRUE
;
532 InsertTailList (&Menu
, &MenuOption
->Link
);
538 Routine used to abstract a generic dialog interface and return the selected key or string
540 @param NumberOfLines The number of lines for the dialog box
541 @param HotKey Defines whether a single character is parsed
542 (TRUE) and returned in KeyValue or a string is
543 returned in StringBuffer. Two special characters
544 are considered when entering a string, a SCAN_ESC
545 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
546 string input and returns
547 @param MaximumStringSize The maximum size in bytes of a typed in string
548 (each character is a CHAR16) and the minimum
549 string returned is two bytes
550 @param StringBuffer The passed in pointer to the buffer which will
551 hold the typed in string if HotKey is FALSE
552 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
553 @param ... A series of (quantity == NumberOfLines) text
554 strings which will be used to construct the dialog
557 @retval EFI_SUCCESS Displayed dialog and received user interaction
558 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
559 (StringBuffer == NULL) && (HotKey == FALSE))
560 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
565 IN UINTN NumberOfLines
,
567 IN UINTN MaximumStringSize
,
568 OUT CHAR16
*StringBuffer
,
569 OUT EFI_INPUT_KEY
*KeyValue
,
574 VA_LIST MarkerBackup
;
579 CHAR16
*BufferedString
;
586 BOOLEAN SelectionComplete
;
588 UINTN CurrentAttribute
;
589 UINTN DimensionsWidth
;
590 UINTN DimensionsHeight
;
592 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
593 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
595 SelectionComplete
= FALSE
;
597 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
598 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
599 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
602 ASSERT (BufferedString
);
604 VA_START (Marker
, KeyValue
);
605 MarkerBackup
= Marker
;
608 // Zero the outgoing buffer
610 ZeroMem (StringBuffer
, MaximumStringSize
);
613 if (KeyValue
== NULL
) {
614 return EFI_INVALID_PARAMETER
;
617 if (StringBuffer
== NULL
) {
618 return EFI_INVALID_PARAMETER
;
624 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
629 // Determine the largest string in the dialog box
630 // Notice we are starting with 1 since String is the first string
632 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
633 StackString
= VA_ARG (Marker
, CHAR16
*);
635 if (StackString
[0] == L
' ') {
636 InputOffset
= Count
+ 1;
639 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
641 // Size of the string visually and subtract the width by one for the null-terminator
643 LargestString
= (GetStringWidth (StackString
) / 2);
647 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
648 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
655 CreateSharedPopUp (LargestString
, NumberOfLines
, MarkerBackup
);
658 // Take the first key typed and report it back?
661 Status
= WaitForKeyStroke (&Key
);
662 ASSERT_EFI_ERROR (Status
);
663 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
667 Status
= WaitForKeyStroke (&Key
);
669 switch (Key
.UnicodeChar
) {
671 switch (Key
.ScanCode
) {
673 gBS
->FreePool (TempString
);
674 gBS
->FreePool (BufferedString
);
675 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
676 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
677 return EFI_DEVICE_ERROR
;
685 case CHAR_CARRIAGE_RETURN
:
686 SelectionComplete
= TRUE
;
687 gBS
->FreePool (TempString
);
688 gBS
->FreePool (BufferedString
);
689 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
690 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
695 if (StringBuffer
[0] != CHAR_NULL
) {
696 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
697 TempString
[Index
] = StringBuffer
[Index
];
700 // Effectively truncate string by 1 character
702 TempString
[Index
- 1] = CHAR_NULL
;
703 StrCpy (StringBuffer
, TempString
);
708 // If it is the beginning of the string, don't worry about checking maximum limits
710 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
711 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
712 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
713 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
714 KeyPad
[0] = Key
.UnicodeChar
;
715 KeyPad
[1] = CHAR_NULL
;
716 StrCat (StringBuffer
, KeyPad
);
717 StrCat (TempString
, KeyPad
);
720 // If the width of the input string is now larger than the screen, we nee to
721 // adjust the index to start printing portions of the string
723 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
725 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
727 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
728 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
733 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
734 BufferedString
[Count
] = StringBuffer
[Index
];
737 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
740 } while (!SelectionComplete
);
743 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
744 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
749 Draw a pop up windows based on the dimension, number of lines and
752 @param RequestedWidth The width of the pop-up.
753 @param NumberOfLines The number of lines.
754 @param Marker The variable argument list for the list of string to be printed.
759 IN UINTN RequestedWidth
,
760 IN UINTN NumberOfLines
,
772 UINTN DimensionsWidth
;
773 UINTN DimensionsHeight
;
775 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
776 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
778 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
780 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
781 RequestedWidth
= DimensionsWidth
- 2;
785 // Subtract the PopUp width from total Columns, allow for one space extra on
786 // each end plus a border.
788 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
789 End
= Start
+ RequestedWidth
+ 1;
791 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
792 Bottom
= Top
+ NumberOfLines
+ 2;
794 Character
= BOXDRAW_DOWN_RIGHT
;
795 PrintCharAt (Start
, Top
, Character
);
796 Character
= BOXDRAW_HORIZONTAL
;
797 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
798 PrintChar (Character
);
801 Character
= BOXDRAW_DOWN_LEFT
;
802 PrintChar (Character
);
803 Character
= BOXDRAW_VERTICAL
;
806 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
807 String
= VA_ARG (Marker
, CHAR16
*);
810 // This will clear the background of the line - we never know who might have been
811 // here before us. This differs from the next clear in that it used the non-reverse
812 // video for normal printing.
814 if (GetStringWidth (String
) / 2 > 1) {
815 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
819 // Passing in a space results in the assumption that this is where typing will occur
821 if (String
[0] == L
' ') {
822 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
826 // Passing in a NULL results in a blank space
828 if (String
[0] == CHAR_NULL
) {
829 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
833 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
837 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
838 PrintCharAt (Start
, Index
+ 1, Character
);
839 PrintCharAt (End
- 1, Index
+ 1, Character
);
842 Character
= BOXDRAW_UP_RIGHT
;
843 PrintCharAt (Start
, Bottom
- 1, Character
);
844 Character
= BOXDRAW_HORIZONTAL
;
845 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
846 PrintChar (Character
);
849 Character
= BOXDRAW_UP_LEFT
;
850 PrintChar (Character
);
854 Draw a pop up windows based on the dimension, number of lines and
857 @param RequestedWidth The width of the pop-up.
858 @param NumberOfLines The number of lines.
859 @param ... A series of text strings that displayed in the pop-up.
864 IN UINTN RequestedWidth
,
865 IN UINTN NumberOfLines
,
871 VA_START (Marker
, NumberOfLines
);
873 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
880 Update status bar on the bottom of menu.
882 @param MessageType The type of message to be shown.
883 @param Flags The flags in Question header.
884 @param State Set or clear.
889 IN UINTN MessageType
,
895 STATIC BOOLEAN InputError
;
896 CHAR16
*NvUpdateMessage
;
897 CHAR16
*InputErrorMessage
;
899 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
900 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
902 switch (MessageType
) {
905 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
907 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
908 gScreenDimensions
.BottomRow
- 1,
913 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
914 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
915 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
922 case NV_UPDATE_REQUIRED
:
923 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
925 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
927 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
928 gScreenDimensions
.BottomRow
- 1,
931 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
933 gNvUpdateRequired
= TRUE
;
935 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
);
936 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
938 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
939 gScreenDimensions
.BottomRow
- 1,
944 gNvUpdateRequired
= FALSE
;
949 case REFRESH_STATUS_BAR
:
951 UpdateStatusBar (INPUT_ERROR
, Flags
, TRUE
);
954 if (gNvUpdateRequired
) {
955 UpdateStatusBar (NV_UPDATE_REQUIRED
, Flags
, TRUE
);
963 gBS
->FreePool (InputErrorMessage
);
964 gBS
->FreePool (NvUpdateMessage
);
970 Get the supported width for a particular op-code
972 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
973 @param Handle The handle in the HII database being used
975 @return Returns the number of CHAR16 characters that is support.
980 IN FORM_BROWSER_STATEMENT
*Statement
,
981 IN EFI_HII_HANDLE Handle
991 // See if the second text parameter is really NULL
993 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
994 String
= GetToken (Statement
->TextTwo
, Handle
);
995 Size
= StrLen (String
);
996 gBS
->FreePool (String
);
999 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1000 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1001 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1002 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1003 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1005 // Allow a wide display if text op-code and no secondary text op-code
1007 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1009 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1011 Width
= (UINT16
) gPromptBlockWidth
;
1014 if (Statement
->InSubtitle
) {
1015 Width
-= SUBTITLE_INDENT
;
1022 BOOLEAN GetLineByWidthFinished
= FALSE
;
1025 Will copy LineWidth amount of a string in the OutputString buffer and return the
1026 number of CHAR16 characters that were copied into the OutputString buffer.
1028 @param InputString String description for this option.
1029 @param LineWidth Width of the desired string to extract in CHAR16
1031 @param Index Where in InputString to start the copy process
1032 @param OutputString Buffer to copy the string into
1034 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1039 IN CHAR16
*InputString
,
1040 IN UINT16 LineWidth
,
1041 IN OUT UINTN
*Index
,
1042 OUT CHAR16
**OutputString
1048 if (GetLineByWidthFinished
) {
1049 GetLineByWidthFinished
= FALSE
;
1056 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1059 // Ensure we have got a valid buffer
1061 if (*OutputString
!= NULL
) {
1064 //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.
1065 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1067 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1068 *Index
= *Index
+ 2;
1072 // Fast-forward the string and see if there is a carriage-return in the string
1074 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1078 // Copy the desired LineWidth of data to the output buffer.
1079 // Also make sure that we don't copy more than the string.
1080 // Also make sure that if there are linefeeds, we account for them.
1082 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1083 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1086 // Convert to CHAR16 value and show that we are done with this operation
1088 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1089 if (LineWidth
!= 0) {
1090 GetLineByWidthFinished
= TRUE
;
1093 if (Count2
== LineWidth
) {
1095 // Rewind the string from the maximum size until we see a space to break the line
1097 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1099 if (LineWidth
== 0) {
1107 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1110 // If currently pointing to a space, increment the index to the first non-space character
1113 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1117 *Index
= (UINT16
) (*Index
+ LineWidth
);
1126 Update display lines for a Menu Option.
1128 @param Selection The user's selection.
1129 @param MenuOption The MenuOption to be checked.
1130 @param OptionalString The option string.
1131 @param SkipValue The number of lins to skip.
1135 UpdateOptionSkipLines (
1136 IN UI_MENU_SELECTION
*Selection
,
1137 IN UI_MENU_OPTION
*MenuOption
,
1138 OUT CHAR16
**OptionalString
,
1146 CHAR16
*OutputString
;
1147 CHAR16
*OptionString
;
1150 OptionString
= *OptionalString
;
1151 OutputString
= NULL
;
1153 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1155 if (OptionString
!= NULL
) {
1156 Width
= (UINT16
) gOptionBlockWidth
;
1160 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1162 // If there is more string to process print on the next row and increment the Skip value
1164 if (StrLen (&OptionString
[Index
])) {
1165 if (SkipValue
== 0) {
1168 // Since the Number of lines for this menu entry may or may not be reflected accurately
1169 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1170 // some testing to ensure we are keeping this in-sync.
1172 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1174 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1180 gBS
->FreePool (OutputString
);
1181 if (SkipValue
!= 0) {
1189 *OptionalString
= OptionString
;
1194 Check whether this Menu Option could be highlighted.
1196 This is an internal function.
1198 @param MenuOption The MenuOption to be checked.
1200 @retval TRUE This Menu Option is selectable.
1201 @retval FALSE This Menu Option could not be selected.
1206 UI_MENU_OPTION
*MenuOption
1209 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1210 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1219 Determine if the menu is the last menu that can be selected.
1221 This is an internal function.
1223 @param Direction The scroll direction. False is down. True is up.
1224 @param CurrentPos The current focus.
1226 @return FALSE -- the menu isn't the last menu that can be selected.
1227 @return TRUE -- the menu is the last menu that can be selected.
1232 IN BOOLEAN Direction
,
1233 IN LIST_ENTRY
*CurrentPos
1237 UI_MENU_OPTION
*MenuOption
;
1239 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1241 if (Temp
== &Menu
) {
1245 for (; Temp
!= &Menu
; Temp
= Direction
? Temp
->BackLink
: Temp
->ForwardLink
) {
1246 MenuOption
= MENU_OPTION_FROM_LINK (Temp
);
1247 if (IsSelectable (MenuOption
)) {
1257 Move to next selectable statement.
1259 This is an internal function.
1261 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1262 @param CurrentPosition Current position.
1264 @return The row distance from current MenuOption to next selectable MenuOption.
1268 MoveToNextStatement (
1270 IN OUT LIST_ENTRY
**CurrentPosition
1276 UI_MENU_OPTION
*NextMenuOption
;
1279 Pos
= *CurrentPosition
;
1283 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1284 if (IsSelectable (NextMenuOption
)) {
1287 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1291 Distance
+= NextMenuOption
->Skip
;
1292 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1297 // If we hit end there is still no statement can be focused,
1298 // we go backwards to find the statement can be focused.
1301 Pos
= *CurrentPosition
;
1304 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1305 if (IsSelectable (NextMenuOption
)) {
1308 if ((!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &Menu
) {
1312 Distance
-= NextMenuOption
->Skip
;
1313 Pos
= (!GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1317 *CurrentPosition
= &NextMenuOption
->Link
;
1323 Adjust Data and Time position accordingly.
1324 Data format : [01/02/2004] [11:22:33]
1325 Line number : 0 0 1 0 0 1
1327 This is an internal function.
1329 @param DirectionUp the up or down direction. False is down. True is
1331 @param CurrentPosition Current position. On return: Point to the last
1332 Option (Year or Second) if up; Point to the first
1333 Option (Month or Hour) if down.
1335 @return Return line number to pad. It is possible that we stand on a zero-advance
1336 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1340 AdjustDateAndTimePosition (
1341 IN BOOLEAN DirectionUp
,
1342 IN OUT LIST_ENTRY
**CurrentPosition
1346 LIST_ENTRY
*NewPosition
;
1347 UI_MENU_OPTION
*MenuOption
;
1348 UINTN PadLineNumber
;
1351 NewPosition
= *CurrentPosition
;
1352 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1354 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1355 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1357 // Calculate the distance from current position to the last Date/Time MenuOption
1360 while (MenuOption
->Skip
== 0) {
1362 NewPosition
= NewPosition
->ForwardLink
;
1363 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1367 NewPosition
= *CurrentPosition
;
1370 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1371 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1372 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1373 // checking can be done.
1375 while (Count
++ < 2) {
1376 NewPosition
= NewPosition
->BackLink
;
1380 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1381 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1382 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1383 // checking can be done.
1385 while (Count
-- > 0) {
1386 NewPosition
= NewPosition
->ForwardLink
;
1390 *CurrentPosition
= NewPosition
;
1393 return PadLineNumber
;
1398 Display menu and wait for user to select one menu option, then return it.
1399 If AutoBoot is enabled, then if user doesn't select any option,
1400 after period of time, it will automatically return the first menu option.
1402 @param Selection Menu selection.
1404 @retval EFI_SUCESSS This function always return successfully for now.
1409 IN OUT UI_MENU_SELECTION
*Selection
1415 UINTN DistanceValue
;
1427 CHAR16
*OptionString
;
1428 CHAR16
*OutputString
;
1429 CHAR16
*FormattedString
;
1439 LIST_ENTRY
*TopOfScreen
;
1440 LIST_ENTRY
*SavedListEntry
;
1441 UI_MENU_OPTION
*MenuOption
;
1442 UI_MENU_OPTION
*NextMenuOption
;
1443 UI_MENU_OPTION
*SavedMenuOption
;
1444 UI_MENU_OPTION
*PreviousMenuOption
;
1445 UI_CONTROL_FLAG ControlFlag
;
1446 EFI_SCREEN_DESCRIPTOR LocalScreen
;
1447 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
1448 UI_SCREEN_OPERATION ScreenOperation
;
1449 UINT8 MinRefreshInterval
;
1452 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1453 FORM_BROWSER_STATEMENT
*Statement
;
1455 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
1457 Status
= EFI_SUCCESS
;
1458 FormattedString
= NULL
;
1459 OptionString
= NULL
;
1460 ScreenOperation
= UiNoOperation
;
1462 MinRefreshInterval
= 0;
1465 OutputString
= NULL
;
1470 MenuRefreshEntry
= gMenuRefreshHead
;
1472 NextMenuOption
= NULL
;
1473 PreviousMenuOption
= NULL
;
1474 SavedMenuOption
= NULL
;
1476 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
1478 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
1479 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1480 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1482 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1483 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
1486 Col
= LocalScreen
.LeftColumn
;
1487 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- SCROLL_ARROW_HEIGHT
- 1;
1489 Selection
->TopRow
= TopRow
;
1490 Selection
->BottomRow
= BottomRow
;
1491 Selection
->PromptCol
= Col
;
1492 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1493 Selection
->Statement
= NULL
;
1495 TopOfScreen
= Menu
.ForwardLink
;
1500 // Get user's selection
1502 NewPos
= Menu
.ForwardLink
;
1504 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
1505 UpdateStatusBar (REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
1507 ControlFlag
= CfInitialization
;
1508 Selection
->Action
= UI_ACTION_NONE
;
1510 switch (ControlFlag
) {
1511 case CfInitialization
:
1512 if (IsListEmpty (&Menu
)) {
1513 ControlFlag
= CfReadKey
;
1515 ControlFlag
= CfCheckSelection
;
1519 case CfCheckSelection
:
1520 if (Selection
->Action
!= UI_ACTION_NONE
) {
1521 ControlFlag
= CfExit
;
1523 ControlFlag
= CfRepaint
;
1528 ControlFlag
= CfRefreshHighLight
;
1542 LocalScreen
.LeftColumn
,
1543 LocalScreen
.RightColumn
,
1544 TopRow
- SCROLL_ARROW_HEIGHT
,
1545 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1546 FIELD_TEXT
| FIELD_BACKGROUND
1549 UiFreeRefreshList ();
1550 MinRefreshInterval
= 0;
1552 for (Link
= TopOfScreen
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
1553 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
1554 MenuOption
->Row
= Row
;
1555 MenuOption
->Col
= Col
;
1556 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
1558 Statement
= MenuOption
->ThisTag
;
1559 if (Statement
->InSubtitle
) {
1560 MenuOption
->Col
+= SUBTITLE_INDENT
;
1563 if (MenuOption
->GrayOut
) {
1564 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1566 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1567 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1571 Width
= GetWidth (Statement
, MenuOption
->Handle
);
1574 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1575 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1576 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
1579 // If there is more string to process print on the next row and increment the Skip value
1581 if (StrLen (&MenuOption
->Description
[Index
])) {
1587 gBS
->FreePool (OutputString
);
1596 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1597 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1599 if (OptionString
!= NULL
) {
1600 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1602 // If leading spaces on OptionString - remove the spaces
1604 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
1605 MenuOption
->OptCol
++;
1608 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1609 OptionString
[Count
] = OptionString
[Index
];
1613 OptionString
[Count
] = CHAR_NULL
;
1617 // If Question request refresh, register the op-code
1619 if (Statement
->RefreshInterval
!= 0) {
1621 // Menu will be refreshed at minimal interval of all Questions
1622 // which have refresh request
1624 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
1625 MinRefreshInterval
= Statement
->RefreshInterval
;
1628 if (gMenuRefreshHead
== NULL
) {
1629 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1630 ASSERT (MenuRefreshEntry
!= NULL
);
1631 MenuRefreshEntry
->MenuOption
= MenuOption
;
1632 MenuRefreshEntry
->Selection
= Selection
;
1633 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1634 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1635 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1636 gMenuRefreshHead
= MenuRefreshEntry
;
1639 // Advance to the last entry
1641 for (MenuRefreshEntry
= gMenuRefreshHead
;
1642 MenuRefreshEntry
->Next
!= NULL
;
1643 MenuRefreshEntry
= MenuRefreshEntry
->Next
1646 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
1647 ASSERT (MenuRefreshEntry
->Next
!= NULL
);
1648 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
1649 MenuRefreshEntry
->MenuOption
= MenuOption
;
1650 MenuRefreshEntry
->Selection
= Selection
;
1651 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
1652 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
1653 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1657 Width
= (UINT16
) gOptionBlockWidth
;
1660 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1661 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
1662 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1665 // If there is more string to process print on the next row and increment the Skip value
1667 if (StrLen (&OptionString
[Index
])) {
1671 // Since the Number of lines for this menu entry may or may not be reflected accurately
1672 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1673 // some testing to ensure we are keeping this in-sync.
1675 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1677 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1683 gBS
->FreePool (OutputString
);
1692 gBS
->FreePool (OptionString
);
1695 // If this is a text op with secondary text information
1697 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1698 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
1700 Width
= (UINT16
) gOptionBlockWidth
;
1703 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
1704 if ((Temp
== 0) && (Row
<= BottomRow
)) {
1705 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
1708 // If there is more string to process print on the next row and increment the Skip value
1710 if (StrLen (&StringPtr
[Index
])) {
1714 // Since the Number of lines for this menu entry may or may not be reflected accurately
1715 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1716 // some testing to ensure we are keeping this in-sync.
1718 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1720 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1726 gBS
->FreePool (OutputString
);
1733 gBS
->FreePool (StringPtr
);
1737 // Need to handle the bottom of the display
1739 if (MenuOption
->Skip
> 1) {
1740 Row
+= MenuOption
->Skip
- SkipValue
;
1743 Row
+= MenuOption
->Skip
;
1746 if (Row
> BottomRow
) {
1747 if (!ValueIsScroll (FALSE
, Link
)) {
1751 Row
= BottomRow
+ 1;
1756 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
1761 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1763 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1764 TopRow
- SCROLL_ARROW_HEIGHT
,
1768 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1772 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
1774 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
1775 BottomRow
+ SCROLL_ARROW_HEIGHT
,
1779 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1786 case CfRefreshHighLight
:
1788 // MenuOption: Last menu option that need to remove hilight
1789 // MenuOption is set to NULL in Repaint
1790 // NewPos: Current menu option that need to hilight
1792 ControlFlag
= CfUpdateHelpString
;
1795 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1796 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1798 SavedValue
= Repaint
;
1801 if (Selection
->QuestionId
!= 0) {
1802 NewPos
= Menu
.ForwardLink
;
1803 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1805 while (SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
&& NewPos
->ForwardLink
!= &Menu
) {
1806 NewPos
= NewPos
->ForwardLink
;
1807 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1809 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
1811 // Target Question found, find its MenuOption
1815 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
1816 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1817 Index
+= SavedMenuOption
->Skip
;
1818 Link
= Link
->ForwardLink
;
1821 if (Link
!= NewPos
|| Index
> BottomRow
) {
1823 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1826 for (Index
= TopRow
; Index
<= BottomRow
; ) {
1827 Link
= Link
->BackLink
;
1828 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
1829 Index
+= SavedMenuOption
->Skip
;
1831 TopOfScreen
= Link
->ForwardLink
;
1835 ControlFlag
= CfRepaint
;
1840 // Target Question not found, highlight the default menu option
1842 NewPos
= TopOfScreen
;
1845 Selection
->QuestionId
= 0;
1848 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
1849 if (MenuOption
!= NULL
) {
1851 // Remove highlight on last Menu Option
1853 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1854 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1855 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1856 if (OptionString
!= NULL
) {
1857 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1858 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
1861 // If leading spaces on OptionString - remove the spaces
1863 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1866 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1867 OptionString
[Count
] = OptionString
[Index
];
1871 OptionString
[Count
] = CHAR_NULL
;
1874 Width
= (UINT16
) gOptionBlockWidth
;
1875 OriginalRow
= MenuOption
->Row
;
1877 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1878 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1879 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1882 // If there is more string to process print on the next row and increment the Skip value
1884 if (StrLen (&OptionString
[Index
])) {
1888 gBS
->FreePool (OutputString
);
1891 MenuOption
->Row
= OriginalRow
;
1893 gBS
->FreePool (OptionString
);
1896 if (MenuOption
->GrayOut
) {
1897 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
1898 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
1899 gST
->ConOut
->SetAttribute (gST
->ConOut
, SUBTITLE_TEXT
| FIELD_BACKGROUND
);
1902 OriginalRow
= MenuOption
->Row
;
1903 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
1905 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
1906 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1907 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
1910 // If there is more string to process print on the next row and increment the Skip value
1912 if (StrLen (&MenuOption
->Description
[Index
])) {
1916 gBS
->FreePool (OutputString
);
1919 MenuOption
->Row
= OriginalRow
;
1920 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
1926 // This is only possible if we entered this page and the first menu option is
1927 // a "non-menu" item. In that case, force it UiDown
1929 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
1930 if (!IsSelectable (MenuOption
)) {
1931 ASSERT (ScreenOperation
== UiNoOperation
);
1932 ScreenOperation
= UiDown
;
1933 ControlFlag
= CfScreenOperation
;
1938 // This is the current selected statement
1940 Statement
= MenuOption
->ThisTag
;
1941 Selection
->Statement
= Statement
;
1944 // Set reverse attribute
1946 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
1947 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
1950 // Assuming that we have a refresh linked-list created, lets annotate the
1951 // appropriate entry that we are highlighting with its new attribute. Just prior to this
1952 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
1954 if (gMenuRefreshHead
!= NULL
) {
1955 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
1956 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT
| FIELD_BACKGROUND
;
1957 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
1958 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
;
1963 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1964 if (OptionString
!= NULL
) {
1965 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
1967 // If leading spaces on OptionString - remove the spaces
1969 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
1972 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
1973 OptionString
[Count
] = OptionString
[Index
];
1977 OptionString
[Count
] = CHAR_NULL
;
1979 Width
= (UINT16
) gOptionBlockWidth
;
1981 OriginalRow
= MenuOption
->Row
;
1983 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1984 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
1985 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
1988 // If there is more string to process print on the next row and increment the Skip value
1990 if (StrLen (&OptionString
[Index
])) {
1994 gBS
->FreePool (OutputString
);
1997 MenuOption
->Row
= OriginalRow
;
1999 gBS
->FreePool (OptionString
);
2002 OriginalRow
= MenuOption
->Row
;
2004 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2006 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2007 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2008 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2011 // If there is more string to process print on the next row and increment the Skip value
2013 if (StrLen (&MenuOption
->Description
[Index
])) {
2017 gBS
->FreePool (OutputString
);
2020 MenuOption
->Row
= OriginalRow
;
2025 if (((NewPos
->ForwardLink
!= &Menu
) && (ScreenOperation
== UiDown
)) ||
2026 ((NewPos
->BackLink
!= &Menu
) && (ScreenOperation
== UiUp
)) ||
2027 (ScreenOperation
== UiNoOperation
)
2029 UpdateKeyHelp (MenuOption
, FALSE
);
2032 // Clear reverse attribute
2034 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
2037 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2038 // if we didn't break halfway when process CfRefreshHighLight.
2040 Repaint
= SavedValue
;
2043 case CfUpdateHelpString
:
2044 ControlFlag
= CfPrepareToReadKey
;
2046 if ((Repaint
|| NewLine
) && (gClassOfVfr
!= EFI_GENERAL_APPLICATION_SUBCLASS
)) {
2048 // Don't print anything if it is a NULL help token
2050 if (MenuOption
->ThisTag
->Help
== 0) {
2053 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2056 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2058 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2060 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2062 // Pad String with spaces to simulate a clearing of the previous line
2064 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2065 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2069 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2071 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2076 // Reset this flag every time we finish using it.
2082 case CfPrepareToReadKey
:
2083 ControlFlag
= CfReadKey
;
2084 ScreenOperation
= UiNoOperation
;
2088 ControlFlag
= CfScreenOperation
;
2091 // Wait for user's selection
2094 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2095 } while (Status
== EFI_TIMEOUT
);
2097 if (Status
== EFI_TIMEOUT
) {
2098 Key
.UnicodeChar
= CHAR_CARRIAGE_RETURN
;
2100 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2102 // if we encounter error, continue to read another key in.
2104 if (EFI_ERROR (Status
)) {
2105 ControlFlag
= CfReadKey
;
2110 if (IsListEmpty (&Menu
) && Key
.UnicodeChar
!= CHAR_NULL
) {
2112 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2117 switch (Key
.UnicodeChar
) {
2118 case CHAR_CARRIAGE_RETURN
:
2119 ScreenOperation
= UiSelect
;
2124 // We will push the adjustment of these numeric values directly to the input handler
2125 // NOTE: we won't handle manual input numeric
2129 Statement
= MenuOption
->ThisTag
;
2130 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2131 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2132 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2134 if (Key
.UnicodeChar
== '+') {
2135 gDirection
= SCAN_RIGHT
;
2137 gDirection
= SCAN_LEFT
;
2139 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2140 SafeFreePool (OptionString
);
2145 ScreenOperation
= UiUp
;
2150 ScreenOperation
= UiDown
;
2154 if (gClassOfVfr
!= EFI_FRONT_PAGE_SUBCLASS
) {
2155 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2156 ScreenOperation
= UiSelect
;
2162 if (((Key
.ScanCode
== SCAN_F1
) && ((gFunctionKeySetting
& FUNCTION_ONE
) != FUNCTION_ONE
)) ||
2163 ((Key
.ScanCode
== SCAN_F2
) && ((gFunctionKeySetting
& FUNCTION_TWO
) != FUNCTION_TWO
)) ||
2164 ((Key
.ScanCode
== SCAN_F9
) && ((gFunctionKeySetting
& FUNCTION_NINE
) != FUNCTION_NINE
)) ||
2165 ((Key
.ScanCode
== SCAN_F10
) && ((gFunctionKeySetting
& FUNCTION_TEN
) != FUNCTION_TEN
))
2168 // If the function key has been disabled, just ignore the key.
2171 for (Index
= 0; Index
< sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]); Index
++) {
2172 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2173 if (Key
.ScanCode
== SCAN_F9
) {
2175 // Reset to standard default
2177 DefaultId
= EFI_HII_DEFAULT_CLASS_STANDARD
;
2179 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2188 case CfScreenOperation
:
2189 if (ScreenOperation
!= UiPrevious
&& ScreenOperation
!= UiReset
) {
2191 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2192 // ignore the selection and go back to reading keys.
2194 if (IsListEmpty (&Menu
)) {
2195 ControlFlag
= CfReadKey
;
2199 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2201 for (Link
= Menu
.ForwardLink
; Link
!= &Menu
; Link
= Link
->ForwardLink
) {
2202 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2203 if (IsSelectable (NextMenuOption
)) {
2208 if (Link
== &Menu
) {
2209 ControlFlag
= CfPrepareToReadKey
;
2212 } else if (ScreenOperation
== UiReset
) {
2214 // Press ESC to exit FormSet
2216 Selection
->Action
= UI_ACTION_EXIT
;
2217 Selection
->Statement
= NULL
;
2221 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2224 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2225 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2232 ControlFlag
= CfCheckSelection
;
2234 if (IsListEmpty (&gMenuList
)) {
2235 Selection
->Action
= UI_ACTION_NONE
;
2236 if (IsListEmpty (&Menu
)) {
2237 ControlFlag
= CfReadKey
;
2243 // Remove the Cached page entry
2245 UiRemoveMenuListEntry (Selection
);
2247 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2248 Selection
->Statement
= NULL
;
2252 ControlFlag
= CfCheckSelection
;
2254 Statement
= MenuOption
->ThisTag
;
2255 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) ||
2256 (Statement
->Operand
== EFI_IFR_DATE_OP
) ||
2257 (Statement
->Operand
== EFI_IFR_TIME_OP
) ||
2258 (Statement
->Operand
== EFI_IFR_NUMERIC_OP
&& Statement
->Step
!= 0)) {
2263 // Keep highlight on current MenuOption
2265 Selection
->QuestionId
= Statement
->QuestionId
;
2267 switch (Statement
->Operand
) {
2268 case EFI_IFR_REF_OP
:
2269 if (Statement
->RefDevicePath
!= 0) {
2271 // Goto another Hii Package list
2273 ControlFlag
= CfUiReset
;
2274 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2276 StringPtr
= GetToken (Statement
->RefDevicePath
, Selection
->FormSet
->HiiHandle
);
2277 if (StringPtr
== NULL
) {
2279 // No device path string not found, exit
2281 Selection
->Action
= UI_ACTION_EXIT
;
2282 Selection
->Statement
= NULL
;
2285 BufferSize
= StrLen (StringPtr
) / 2;
2286 DevicePath
= AllocatePool (BufferSize
);
2288 HexStringToBufInReverseOrder ((UINT8
*) DevicePath
, &BufferSize
, StringPtr
);
2289 Selection
->Handle
= HiiLibDevicePathToHiiHandle (DevicePath
);
2290 if (Selection
->Handle
== NULL
) {
2292 // If target Hii Handle not found, exit
2294 Selection
->Action
= UI_ACTION_EXIT
;
2295 Selection
->Statement
= NULL
;
2299 gBS
->FreePool (StringPtr
);
2300 gBS
->FreePool (DevicePath
);
2302 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2303 Selection
->FormId
= Statement
->RefFormId
;
2304 Selection
->QuestionId
= Statement
->RefQuestionId
;
2305 } else if (!CompareGuid (&Statement
->RefFormSetId
, &gZeroGuid
)) {
2307 // Goto another Formset, check for uncommitted data
2309 ControlFlag
= CfUiReset
;
2310 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2312 CopyMem (&Selection
->FormSetGuid
, &Statement
->RefFormSetId
, sizeof (EFI_GUID
));
2313 Selection
->FormId
= Statement
->RefFormId
;
2314 Selection
->QuestionId
= Statement
->RefQuestionId
;
2315 } else if (Statement
->RefFormId
!= 0) {
2317 // Goto another form inside this formset,
2319 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2322 // Link current form so that we can always go back when someone hits the UiPrevious
2324 UiAddMenuListEntry (Selection
);
2326 Selection
->FormId
= Statement
->RefFormId
;
2327 Selection
->QuestionId
= Statement
->RefQuestionId
;
2328 } else if (Statement
->RefQuestionId
!= 0) {
2330 // Goto another Question
2332 Selection
->QuestionId
= Statement
->RefQuestionId
;
2334 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2335 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2344 case EFI_IFR_ACTION_OP
:
2346 // Process the Config string <ConfigResp>
2348 Status
= ProcessQuestionConfig (Selection
, Statement
);
2350 if (EFI_ERROR (Status
)) {
2355 // The action button may change some Question value, so refresh the form
2357 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2360 case EFI_IFR_RESET_BUTTON_OP
:
2362 // Reset Question to default value specified by DefaultId
2364 ControlFlag
= CfUiDefault
;
2365 DefaultId
= Statement
->DefaultId
;
2370 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2372 UpdateKeyHelp (MenuOption
, TRUE
);
2373 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2375 if (EFI_ERROR (Status
)) {
2381 if (OptionString
!= NULL
) {
2382 PrintStringAt (LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ 1, MenuOption
->Row
, OptionString
);
2383 gBS
->FreePool (OptionString
);
2386 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2393 // We are going to leave current FormSet, so check uncommited data in this FormSet
2395 ControlFlag
= CfCheckSelection
;
2397 if (gClassOfVfr
== EFI_FRONT_PAGE_SUBCLASS
) {
2399 // There is no parent menu for FrontPage
2401 Selection
->Action
= UI_ACTION_NONE
;
2402 Selection
->Statement
= MenuOption
->ThisTag
;
2407 // If NV flag is up, prompt user
2409 if (gNvUpdateRequired
) {
2410 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2412 YesResponse
= gYesResponse
[0];
2413 NoResponse
= gNoResponse
[0];
2416 CreateDialog (3, TRUE
, 0, NULL
, &Key
, gEmptyString
, gAreYouSure
, gEmptyString
);
2419 (Key
.ScanCode
!= SCAN_ESC
) &&
2420 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (NoResponse
| UPPER_LOWER_CASE_OFFSET
)) &&
2421 ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) != (YesResponse
| UPPER_LOWER_CASE_OFFSET
))
2425 // If the user hits the YesResponse key
2427 if ((Key
.UnicodeChar
| UPPER_LOWER_CASE_OFFSET
) == (YesResponse
| UPPER_LOWER_CASE_OFFSET
)) {
2432 Selection
->Action
= UI_ACTION_NONE
;
2437 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2438 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2441 gST
->ConOut
->ClearScreen (gST
->ConOut
);
2445 ControlFlag
= CfCheckSelection
;
2446 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2447 if (MenuOption
->Sequence
!= 0) {
2449 // In the middle or tail of the Date/Time op-code set, go left.
2451 NewPos
= NewPos
->BackLink
;
2457 ControlFlag
= CfCheckSelection
;
2458 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
2459 if (MenuOption
->Sequence
!= 2) {
2461 // In the middle or tail of the Date/Time op-code set, go left.
2463 NewPos
= NewPos
->ForwardLink
;
2469 ControlFlag
= CfCheckSelection
;
2471 SavedListEntry
= TopOfScreen
;
2473 if (NewPos
->BackLink
!= &Menu
) {
2476 // Adjust Date/Time position before we advance forward.
2478 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2481 // Caution that we have already rewind to the top, don't go backward in this situation.
2483 if (NewPos
->BackLink
!= &Menu
) {
2484 NewPos
= NewPos
->BackLink
;
2487 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2488 DistanceValue
= PreviousMenuOption
->Skip
;
2491 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2492 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2493 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2494 // checking can be done.
2496 DistanceValue
+= AdjustDateAndTimePosition (TRUE
, &NewPos
);
2499 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2500 // don't worry about a redraw.
2502 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2504 TopOfScreen
= NewPos
;
2507 Difference
= MoveToNextStatement (TRUE
, &NewPos
);
2508 if ((INTN
) MenuOption
->Row
- (INTN
) DistanceValue
< (INTN
) TopRow
) {
2509 if (Difference
> 0) {
2511 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2513 TopOfScreen
= NewPos
;
2517 if (Difference
< 0) {
2519 // We want to goto previous MenuOption, but finally we go down.
2520 // it means that we hit the begining MenuOption that can be focused
2521 // so we simply scroll to the top
2523 if (SavedListEntry
!= Menu
.ForwardLink
) {
2524 TopOfScreen
= Menu
.ForwardLink
;
2530 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2532 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2534 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2536 SavedMenuOption
= MenuOption
;
2537 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2538 if (!IsSelectable (MenuOption
)) {
2540 // If we are at the end of the list and sitting on a text op, we need to more forward
2542 ScreenOperation
= UiDown
;
2543 ControlFlag
= CfScreenOperation
;
2547 MenuOption
= SavedMenuOption
;
2552 ControlFlag
= CfCheckSelection
;
2554 if (NewPos
->BackLink
== &Menu
) {
2563 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2565 while ((Index
>= TopRow
) && (Link
->BackLink
!= &Menu
)) {
2566 Index
= Index
- PreviousMenuOption
->Skip
;
2567 Link
= Link
->BackLink
;
2568 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2572 Difference
= MoveToNextStatement (TRUE
, &Link
);
2573 if (Difference
> 0) {
2575 // The focus MenuOption is above the TopOfScreen
2578 } else if (Difference
< 0) {
2580 // This happens when there is no MenuOption can be focused from
2581 // Current MenuOption to the first MenuOption
2583 TopOfScreen
= Menu
.ForwardLink
;
2585 Index
+= Difference
;
2586 if (Index
< TopRow
) {
2590 if (NewPos
== Link
) {
2598 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2599 // Don't do this when we are already in the first page.
2601 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2602 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2606 ControlFlag
= CfCheckSelection
;
2608 if (NewPos
->ForwardLink
== &Menu
) {
2617 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2619 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &Menu
)) {
2620 Index
= Index
+ NextMenuOption
->Skip
;
2621 Link
= Link
->ForwardLink
;
2622 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2625 Index
+= MoveToNextStatement (FALSE
, &Link
);
2626 if (Index
> BottomRow
) {
2628 // There are more MenuOption needing scrolling
2633 if (NewPos
== Link
&& Index
<= BottomRow
) {
2635 // Finally we know that NewPos is the last MenuOption can be focused.
2644 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2645 // Don't do this when we are already in the last page.
2647 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
2648 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2652 ControlFlag
= CfCheckSelection
;
2654 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2655 // to be one that progresses to the next set of op-codes, we need to advance to the last
2656 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2657 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2658 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2659 // the Date/Time op-code.
2661 SavedListEntry
= NewPos
;
2662 DistanceValue
= AdjustDateAndTimePosition (FALSE
, &NewPos
);
2664 if (NewPos
->ForwardLink
!= &Menu
) {
2665 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2667 NewPos
= NewPos
->ForwardLink
;
2668 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2670 DistanceValue
+= NextMenuOption
->Skip
;
2671 DistanceValue
+= MoveToNextStatement (FALSE
, &NewPos
);
2673 // An option might be multi-line, so we need to reflect that data in the overall skip value
2675 UpdateOptionSkipLines (Selection
, NextMenuOption
, &OptionString
, SkipValue
);
2677 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
2678 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
2679 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
2680 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2686 // If we are going to scroll, update TopOfScreen
2688 if (Temp
> BottomRow
) {
2691 // Is the current top of screen a zero-advance op-code?
2692 // If so, keep moving forward till we hit a >0 advance op-code
2694 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2697 // If bottom op-code is more than one line or top op-code is more than one line
2699 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
2701 // Is the bottom op-code greater than or equal in size to the top op-code?
2703 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
2705 // Skip the top op-code
2707 TopOfScreen
= TopOfScreen
->ForwardLink
;
2708 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
2710 OldSkipValue
= Difference
;
2712 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2715 // If we have a remainder, skip that many more op-codes until we drain the remainder
2718 Difference
>= (INTN
) SavedMenuOption
->Skip
;
2719 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
2722 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2724 TopOfScreen
= TopOfScreen
->ForwardLink
;
2725 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2726 if (Difference
< (INTN
) SavedMenuOption
->Skip
) {
2727 Difference
= SavedMenuOption
->Skip
- Difference
- 1;
2730 if (Difference
== (INTN
) SavedMenuOption
->Skip
) {
2731 TopOfScreen
= TopOfScreen
->ForwardLink
;
2732 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
2733 Difference
= SavedMenuOption
->Skip
- Difference
;
2739 // Since we will act on this op-code in the next routine, and increment the
2740 // SkipValue, set the skips to one less than what is required.
2742 SkipValue
= Difference
- 1;
2746 // Since we will act on this op-code in the next routine, and increment the
2747 // SkipValue, set the skips to one less than what is required.
2749 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
2752 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
2753 TopOfScreen
= TopOfScreen
->ForwardLink
;
2756 SkipValue
= OldSkipValue
;
2760 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2761 // Let's set a skip flag to smoothly scroll the top of the screen.
2763 if (SavedMenuOption
->Skip
> 1) {
2764 if (SavedMenuOption
== NextMenuOption
) {
2771 TopOfScreen
= TopOfScreen
->ForwardLink
;
2773 } while (SavedMenuOption
->Skip
== 0);
2776 OldSkipValue
= SkipValue
;
2779 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
2781 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2784 SavedMenuOption
= MenuOption
;
2785 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2786 if (!IsSelectable (MenuOption
)) {
2788 // If we are at the end of the list and sitting on a text op, we need to more forward
2790 ScreenOperation
= UiUp
;
2791 ControlFlag
= CfScreenOperation
;
2795 MenuOption
= SavedMenuOption
;
2797 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2799 AdjustDateAndTimePosition (TRUE
, &NewPos
);
2804 ControlFlag
= CfCheckSelection
;
2809 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
);
2811 if (!EFI_ERROR (Status
)) {
2812 UpdateStatusBar (INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2813 UpdateStatusBar (NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
2816 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gSaveFailed
, gPressEnter
, gEmptyString
);
2817 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2825 ControlFlag
= CfCheckSelection
;
2827 Status
= ExtractFormDefault (Selection
->FormSet
, Selection
->Form
, DefaultId
);
2829 if (!EFI_ERROR (Status
)) {
2830 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2831 Selection
->Statement
= NULL
;
2834 // Show NV update flag on status bar
2836 gNvUpdateRequired
= TRUE
;
2840 case CfUiNoOperation
:
2841 ControlFlag
= CfCheckSelection
;
2845 UiFreeRefreshList ();
2847 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
2848 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
2849 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
2850 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");