2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2011, Intel Corporation. All rights reserved.<BR>
5 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.
17 LIST_ENTRY gMenuOption
;
18 LIST_ENTRY gMenuList
= INITIALIZE_LIST_HEAD_VARIABLE (gMenuList
);
19 MENU_REFRESH_ENTRY
*gMenuRefreshHead
; // Menu list used for refresh timer opcode.
20 MENU_REFRESH_ENTRY
*gMenuEventGuidRefreshHead
; // Menu list used for refresh event guid opcode.
23 // Search table for UiDisplayMenu()
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation
[] = {
56 UINTN mScanCodeNumber
= sizeof (gScanCodeToOperation
) / sizeof (gScanCodeToOperation
[0]);
58 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag
[] = {
102 BOOLEAN GetLineByWidthFinished
= FALSE
;
106 Set Buffer to Value for Size bytes.
108 @param Buffer Memory to set.
109 @param Size Number of bytes to set
110 @param Value Value of the set operation.
123 while ((Size
--) != 0) {
130 Initialize Menu option list.
138 InitializeListHead (&gMenuOption
);
143 Free Menu option linked list.
151 UI_MENU_OPTION
*MenuOption
;
153 while (!IsListEmpty (&gMenuOption
)) {
154 MenuOption
= MENU_OPTION_FROM_LINK (gMenuOption
.ForwardLink
);
155 RemoveEntryList (&MenuOption
->Link
);
158 // We allocated space for this description when we did a GetToken, free it here
160 if (MenuOption
->Skip
!= 0) {
162 // For date/time, MenuOption->Description is shared by three Menu Options
163 // Data format : [01/02/2004] [11:22:33]
164 // Line number : 0 0 1 0 0 1
166 FreePool (MenuOption
->Description
);
168 FreePool (MenuOption
);
174 Create a menu with specified formset GUID and form ID, and add it as a child
175 of the given parent menu.
177 @param Parent The parent of menu to be added.
178 @param HiiHandle Hii handle related to this formset.
179 @param FormSetGuid The Formset Guid of menu to be added.
180 @param FormId The Form ID of menu to be added.
182 @return A pointer to the newly added menu or NULL if memory is insufficient.
187 IN OUT UI_MENU_LIST
*Parent
,
188 IN EFI_HII_HANDLE HiiHandle
,
189 IN EFI_GUID
*FormSetGuid
,
193 UI_MENU_LIST
*MenuList
;
195 MenuList
= AllocateZeroPool (sizeof (UI_MENU_LIST
));
196 if (MenuList
== NULL
) {
200 MenuList
->Signature
= UI_MENU_LIST_SIGNATURE
;
201 InitializeListHead (&MenuList
->ChildListHead
);
203 MenuList
->HiiHandle
= HiiHandle
;
204 CopyMem (&MenuList
->FormSetGuid
, FormSetGuid
, sizeof (EFI_GUID
));
205 MenuList
->FormId
= FormId
;
206 MenuList
->Parent
= Parent
;
208 if (Parent
== NULL
) {
210 // If parent is not specified, it is the root Form of a Formset
212 InsertTailList (&gMenuList
, &MenuList
->Link
);
214 InsertTailList (&Parent
->ChildListHead
, &MenuList
->Link
);
222 Search Menu with given FormId and FormSetGuid in all cached menu list.
224 @param Parent The parent of menu to search.
225 @param FormSetGuid The Formset GUID of the menu to search.
226 @param FormId The Form ID of menu to search.
228 @return A pointer to menu found or NULL if not found.
232 UiFindChildMenuList (
233 IN UI_MENU_LIST
*Parent
,
234 IN EFI_GUID
*FormSetGuid
,
240 UI_MENU_LIST
*MenuList
;
242 ASSERT (Parent
!= NULL
);
244 if (Parent
->FormId
== FormId
&& CompareGuid (FormSetGuid
, &Parent
->FormSetGuid
)) {
248 Link
= GetFirstNode (&Parent
->ChildListHead
);
249 while (!IsNull (&Parent
->ChildListHead
, Link
)) {
250 Child
= UI_MENU_LIST_FROM_LINK (Link
);
252 MenuList
= UiFindChildMenuList (Child
, FormSetGuid
, FormId
);
253 if (MenuList
!= NULL
) {
257 Link
= GetNextNode (&Parent
->ChildListHead
, Link
);
265 Search Menu with given FormSetGuid and FormId in all cached menu list.
267 @param FormSetGuid The Formset GUID of the menu to search.
268 @param FormId The Form ID of menu to search.
270 @return A pointer to menu found or NULL if not found.
275 IN EFI_GUID
*FormSetGuid
,
280 UI_MENU_LIST
*MenuList
;
283 Link
= GetFirstNode (&gMenuList
);
284 while (!IsNull (&gMenuList
, Link
)) {
285 MenuList
= UI_MENU_LIST_FROM_LINK (Link
);
287 Child
= UiFindChildMenuList(MenuList
, FormSetGuid
, FormId
);
292 Link
= GetNextNode (&gMenuList
, Link
);
300 Free Menu option linked list.
308 MENU_REFRESH_ENTRY
*OldMenuRefreshEntry
;
310 while (gMenuRefreshHead
!= NULL
) {
311 OldMenuRefreshEntry
= gMenuRefreshHead
->Next
;
312 FreePool (gMenuRefreshHead
);
313 gMenuRefreshHead
= OldMenuRefreshEntry
;
316 while (gMenuEventGuidRefreshHead
!= NULL
) {
317 OldMenuRefreshEntry
= gMenuEventGuidRefreshHead
->Next
;
318 if (gMenuEventGuidRefreshHead
!= NULL
) {
319 gBS
->CloseEvent(gMenuEventGuidRefreshHead
->Event
);
321 FreePool (gMenuEventGuidRefreshHead
);
322 gMenuEventGuidRefreshHead
= OldMenuRefreshEntry
;
331 @param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
335 IN MENU_REFRESH_ENTRY
*MenuRefreshEntry
338 CHAR16
*OptionString
;
341 UI_MENU_SELECTION
*Selection
;
342 FORM_BROWSER_STATEMENT
*Question
;
344 Selection
= MenuRefreshEntry
->Selection
;
345 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
347 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
348 if (EFI_ERROR (Status
)) {
353 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
355 if (OptionString
!= NULL
) {
357 // If leading spaces on OptionString - remove the spaces
359 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
363 // If old Text is longer than new string, need to clean the old string before paint the newer.
364 // This option is no need for time/date opcode, because time/data opcode has fixed string length.
366 if ((MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_DATE_OP
) &&
367 (MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_TIME_OP
)) {
369 MenuRefreshEntry
->CurrentColumn
,
370 MenuRefreshEntry
->CurrentColumn
+ gOptionBlockWidth
- 1,
371 MenuRefreshEntry
->CurrentRow
,
372 MenuRefreshEntry
->CurrentRow
,
373 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
377 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
378 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, &OptionString
[Index
]);
379 FreePool (OptionString
);
383 // Question value may be changed, need invoke its Callback()
385 Status
= ProcessCallBackFunction (Selection
, Question
, EFI_BROWSER_ACTION_CHANGING
, FALSE
);
391 Refresh the question which has refresh guid event attribute.
393 @param Event The event which has this function related.
394 @param Context The input context info related to this event or the status code return to the caller.
398 RefreshQuestionNotify(
403 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
404 UI_MENU_SELECTION
*Selection
;
407 // Reset FormPackage update flag
409 mHiiPackageListUpdated
= FALSE
;
411 MenuRefreshEntry
= (MENU_REFRESH_ENTRY
*)Context
;
412 ASSERT (MenuRefreshEntry
!= NULL
);
413 Selection
= MenuRefreshEntry
->Selection
;
415 RefreshQuestion (MenuRefreshEntry
);
417 if (mHiiPackageListUpdated
) {
419 // Package list is updated, force to reparse IFR binary of target Formset
421 mHiiPackageListUpdated
= FALSE
;
422 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
436 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
438 UI_MENU_SELECTION
*Selection
;
440 if (gMenuRefreshHead
!= NULL
) {
442 // call from refresh interval process.
444 MenuRefreshEntry
= gMenuRefreshHead
;
445 Selection
= MenuRefreshEntry
->Selection
;
447 // Reset FormPackage update flag
449 mHiiPackageListUpdated
= FALSE
;
452 Status
= RefreshQuestion (MenuRefreshEntry
);
453 if (EFI_ERROR (Status
)) {
457 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
459 } while (MenuRefreshEntry
!= NULL
);
461 if (mHiiPackageListUpdated
) {
463 // Package list is updated, force to reparse IFR binary of target Formset
465 mHiiPackageListUpdated
= FALSE
;
466 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
476 Wait for a given event to fire, or for an optional timeout to expire.
478 @param Event The event to wait for
479 @param Timeout An optional timeout value in 100 ns units.
480 @param RefreshInterval Menu refresh interval (in seconds).
482 @retval EFI_SUCCESS Event fired before Timeout expired.
483 @retval EFI_TIME_OUT Timout expired before Event fired.
487 UiWaitForSingleEvent (
489 IN UINT64 Timeout
, OPTIONAL
490 IN UINT8 RefreshInterval OPTIONAL
495 EFI_EVENT TimerEvent
;
496 EFI_EVENT WaitList
[2];
500 // Create a timer event
502 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
503 if (!EFI_ERROR (Status
)) {
505 // Set the timer event
514 // Wait for the original event or the timer
517 WaitList
[1] = TimerEvent
;
518 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
519 gBS
->CloseEvent (TimerEvent
);
522 // If the timer expired, change the return to timed out
524 if (!EFI_ERROR (Status
) && Index
== 1) {
525 Status
= EFI_TIMEOUT
;
530 // Update screen every second
532 if (RefreshInterval
== 0) {
533 Timeout
= ONE_SECOND
;
535 Timeout
= RefreshInterval
* ONE_SECOND
;
539 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
542 // Set the timer event
551 // Wait for the original event or the timer
554 WaitList
[1] = TimerEvent
;
555 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
558 // If the timer expired, update anything that needs a refresh and keep waiting
560 if (!EFI_ERROR (Status
) && Index
== 1) {
561 Status
= EFI_TIMEOUT
;
562 if (RefreshInterval
!= 0) {
563 Status
= RefreshForm ();
567 gBS
->CloseEvent (TimerEvent
);
568 } while (Status
== EFI_TIMEOUT
);
576 Add one menu option by specified description and context.
578 @param String String description for this option.
579 @param Handle Hii handle for the package list.
580 @param Statement Statement of this Menu Option.
581 @param NumberOfLines Display lines for this Menu Option.
582 @param MenuItemCount The index for this Option in the Menu.
584 @retval Pointer Pointer to the added Menu Option.
590 IN EFI_HII_HANDLE Handle
,
591 IN FORM_BROWSER_STATEMENT
*Statement
,
592 IN UINT16 NumberOfLines
,
593 IN UINT16 MenuItemCount
596 UI_MENU_OPTION
*MenuOption
;
603 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
605 // Add three MenuOptions for Date/Time
606 // Data format : [01/02/2004] [11:22:33]
607 // Line number : 0 0 1 0 0 1
612 if (Statement
->Storage
== NULL
) {
614 // For RTC type of date/time, set default refresh interval to be 1 second
616 if (Statement
->RefreshInterval
== 0) {
617 Statement
->RefreshInterval
= 1;
622 for (Index
= 0; Index
< Count
; Index
++) {
623 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
626 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
627 MenuOption
->Description
= String
;
628 MenuOption
->Handle
= Handle
;
629 MenuOption
->ThisTag
= Statement
;
630 MenuOption
->EntryNumber
= MenuItemCount
;
634 // Override LineNumber for the MenuOption in Date/Time sequence
636 MenuOption
->Skip
= 1;
638 MenuOption
->Skip
= NumberOfLines
;
640 MenuOption
->Sequence
= Index
;
642 if (Statement
->GrayOutExpression
!= NULL
) {
643 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
646 switch (Statement
->Operand
) {
647 case EFI_IFR_ORDERED_LIST_OP
:
648 case EFI_IFR_ONE_OF_OP
:
649 case EFI_IFR_NUMERIC_OP
:
650 case EFI_IFR_TIME_OP
:
651 case EFI_IFR_DATE_OP
:
652 case EFI_IFR_CHECKBOX_OP
:
653 case EFI_IFR_PASSWORD_OP
:
654 case EFI_IFR_STRING_OP
:
656 // User could change the value of these items
658 MenuOption
->IsQuestion
= TRUE
;
661 case EFI_IFR_TEXT_OP
:
662 if (FeaturePcdGet (PcdBrowserGrayOutTextStatement
)) {
664 // Initializing GrayOut option as TRUE for Text setup options
665 // so that those options will be Gray in colour and un selectable.
667 MenuOption
->GrayOut
= TRUE
;
671 MenuOption
->IsQuestion
= FALSE
;
675 if ((Statement
->ValueExpression
!= NULL
) ||
676 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
677 MenuOption
->ReadOnly
= TRUE
;
680 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
688 Routine used to abstract a generic dialog interface and return the selected key or string
690 @param NumberOfLines The number of lines for the dialog box
691 @param HotKey Defines whether a single character is parsed
692 (TRUE) and returned in KeyValue or a string is
693 returned in StringBuffer. Two special characters
694 are considered when entering a string, a SCAN_ESC
695 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
696 string input and returns
697 @param MaximumStringSize The maximum size in bytes of a typed in string
698 (each character is a CHAR16) and the minimum
699 string returned is two bytes
700 @param StringBuffer The passed in pointer to the buffer which will
701 hold the typed in string if HotKey is FALSE
702 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
703 @param ... A series of (quantity == NumberOfLines) text
704 strings which will be used to construct the dialog
707 @retval EFI_SUCCESS Displayed dialog and received user interaction
708 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
709 (StringBuffer == NULL) && (HotKey == FALSE))
710 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
716 IN UINTN NumberOfLines
,
718 IN UINTN MaximumStringSize
,
719 OUT CHAR16
*StringBuffer
,
720 OUT EFI_INPUT_KEY
*KeyValue
,
729 CHAR16
*BufferedString
;
736 BOOLEAN SelectionComplete
;
738 UINTN CurrentAttribute
;
739 UINTN DimensionsWidth
;
740 UINTN DimensionsHeight
;
742 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
743 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
745 SelectionComplete
= FALSE
;
747 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
748 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
749 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
752 ASSERT (BufferedString
);
754 VA_START (Marker
, KeyValue
);
757 // Zero the outgoing buffer
759 ZeroMem (StringBuffer
, MaximumStringSize
);
762 if (KeyValue
== NULL
) {
763 return EFI_INVALID_PARAMETER
;
766 if (StringBuffer
== NULL
) {
767 return EFI_INVALID_PARAMETER
;
773 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
778 // Determine the largest string in the dialog box
779 // Notice we are starting with 1 since String is the first string
781 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
782 StackString
= VA_ARG (Marker
, CHAR16
*);
784 if (StackString
[0] == L
' ') {
785 InputOffset
= Count
+ 1;
788 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
790 // Size of the string visually and subtract the width by one for the null-terminator
792 LargestString
= (GetStringWidth (StackString
) / 2);
797 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
798 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
805 VA_START (Marker
, KeyValue
);
806 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
810 // Take the first key typed and report it back?
813 Status
= WaitForKeyStroke (&Key
);
814 ASSERT_EFI_ERROR (Status
);
815 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
819 Status
= WaitForKeyStroke (&Key
);
821 switch (Key
.UnicodeChar
) {
823 switch (Key
.ScanCode
) {
825 FreePool (TempString
);
826 FreePool (BufferedString
);
827 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
828 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
829 return EFI_DEVICE_ERROR
;
837 case CHAR_CARRIAGE_RETURN
:
838 SelectionComplete
= TRUE
;
839 FreePool (TempString
);
840 FreePool (BufferedString
);
841 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
842 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
847 if (StringBuffer
[0] != CHAR_NULL
) {
848 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
849 TempString
[Index
] = StringBuffer
[Index
];
852 // Effectively truncate string by 1 character
854 TempString
[Index
- 1] = CHAR_NULL
;
855 StrCpy (StringBuffer
, TempString
);
860 // If it is the beginning of the string, don't worry about checking maximum limits
862 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
863 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
864 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
865 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
866 KeyPad
[0] = Key
.UnicodeChar
;
867 KeyPad
[1] = CHAR_NULL
;
868 StrCat (StringBuffer
, KeyPad
);
869 StrCat (TempString
, KeyPad
);
872 // If the width of the input string is now larger than the screen, we nee to
873 // adjust the index to start printing portions of the string
875 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
877 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
879 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
880 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
885 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
886 BufferedString
[Count
] = StringBuffer
[Index
];
889 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
892 } while (!SelectionComplete
);
895 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
896 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
901 Draw a pop up windows based on the dimension, number of lines and
904 @param RequestedWidth The width of the pop-up.
905 @param NumberOfLines The number of lines.
906 @param Marker The variable argument list for the list of string to be printed.
911 IN UINTN RequestedWidth
,
912 IN UINTN NumberOfLines
,
924 UINTN DimensionsWidth
;
925 UINTN DimensionsHeight
;
927 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
928 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
930 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
932 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
933 RequestedWidth
= DimensionsWidth
- 2;
937 // Subtract the PopUp width from total Columns, allow for one space extra on
938 // each end plus a border.
940 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
941 End
= Start
+ RequestedWidth
+ 1;
943 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
944 Bottom
= Top
+ NumberOfLines
+ 2;
946 Character
= BOXDRAW_DOWN_RIGHT
;
947 PrintCharAt (Start
, Top
, Character
);
948 Character
= BOXDRAW_HORIZONTAL
;
949 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
950 PrintChar (Character
);
953 Character
= BOXDRAW_DOWN_LEFT
;
954 PrintChar (Character
);
955 Character
= BOXDRAW_VERTICAL
;
958 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
959 String
= VA_ARG (Marker
, CHAR16
*);
962 // This will clear the background of the line - we never know who might have been
963 // here before us. This differs from the next clear in that it used the non-reverse
964 // video for normal printing.
966 if (GetStringWidth (String
) / 2 > 1) {
967 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
971 // Passing in a space results in the assumption that this is where typing will occur
973 if (String
[0] == L
' ') {
974 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
978 // Passing in a NULL results in a blank space
980 if (String
[0] == CHAR_NULL
) {
981 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
985 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
989 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
990 PrintCharAt (Start
, Index
+ 1, Character
);
991 PrintCharAt (End
- 1, Index
+ 1, Character
);
994 Character
= BOXDRAW_UP_RIGHT
;
995 PrintCharAt (Start
, Bottom
- 1, Character
);
996 Character
= BOXDRAW_HORIZONTAL
;
997 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
998 PrintChar (Character
);
1001 Character
= BOXDRAW_UP_LEFT
;
1002 PrintChar (Character
);
1006 Draw a pop up windows based on the dimension, number of lines and
1009 @param RequestedWidth The width of the pop-up.
1010 @param NumberOfLines The number of lines.
1011 @param ... A series of text strings that displayed in the pop-up.
1016 CreateMultiStringPopUp (
1017 IN UINTN RequestedWidth
,
1018 IN UINTN NumberOfLines
,
1024 VA_START (Marker
, NumberOfLines
);
1026 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1033 Update status bar on the bottom of menu.
1035 @param Selection Current Selction info.
1036 @param MessageType The type of message to be shown.
1037 @param Flags The flags in Question header.
1038 @param State Set or clear.
1043 IN UI_MENU_SELECTION
*Selection
,
1044 IN UINTN MessageType
,
1050 CHAR16
*NvUpdateMessage
;
1051 CHAR16
*InputErrorMessage
;
1053 FORM_BROWSER_FORMSET
*LocalFormSet
;
1054 FORM_BROWSER_STATEMENT
*Question
;
1056 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1057 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1059 switch (MessageType
) {
1062 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1064 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1065 gScreenDimensions
.BottomRow
- 1,
1070 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1071 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1072 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1075 mInputError
= FALSE
;
1079 case NV_UPDATE_REQUIRED
:
1081 // Global setting support. Show configuration change on every form.
1084 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1086 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1087 Question
= Selection
->Statement
;
1088 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1090 // Update only for Question value that need to be saved into Storage.
1092 Selection
->Form
->NvUpdateRequired
= TRUE
;
1096 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1097 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1099 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1100 gScreenDimensions
.BottomRow
- 1,
1105 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1106 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1108 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1109 gScreenDimensions
.BottomRow
- 1,
1116 case REFRESH_STATUS_BAR
:
1118 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1121 switch (gBrowserSettingScope
) {
1124 // Check the maintain list to see whether there is any change.
1126 Link
= GetFirstNode (&gBrowserFormSetList
);
1127 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1128 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1129 if (IsNvUpdateRequired(LocalFormSet
)) {
1130 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1133 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1138 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1149 FreePool (InputErrorMessage
);
1150 FreePool (NvUpdateMessage
);
1156 Get the supported width for a particular op-code
1158 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1159 @param Handle The handle in the HII database being used
1161 @return Returns the number of CHAR16 characters that is support.
1166 IN FORM_BROWSER_STATEMENT
*Statement
,
1167 IN EFI_HII_HANDLE Handle
1177 // See if the second text parameter is really NULL
1179 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1180 String
= GetToken (Statement
->TextTwo
, Handle
);
1181 Size
= StrLen (String
);
1185 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1186 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1187 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1188 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1189 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1191 // Allow a wide display if text op-code and no secondary text op-code
1193 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1195 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1197 Width
= (UINT16
) gPromptBlockWidth
;
1200 if (Statement
->InSubtitle
) {
1201 Width
-= SUBTITLE_INDENT
;
1204 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1208 Will copy LineWidth amount of a string in the OutputString buffer and return the
1209 number of CHAR16 characters that were copied into the OutputString buffer.
1211 @param InputString String description for this option.
1212 @param LineWidth Width of the desired string to extract in CHAR16
1214 @param Index Where in InputString to start the copy process
1215 @param OutputString Buffer to copy the string into
1217 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1222 IN CHAR16
*InputString
,
1223 IN UINT16 LineWidth
,
1224 IN OUT UINTN
*Index
,
1225 OUT CHAR16
**OutputString
1231 if (GetLineByWidthFinished
) {
1232 GetLineByWidthFinished
= FALSE
;
1239 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1242 // Ensure we have got a valid buffer
1244 if (*OutputString
!= NULL
) {
1247 //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.
1248 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1250 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1251 *Index
= *Index
+ 2;
1255 // Fast-forward the string and see if there is a carriage-return in the string
1257 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1261 // Copy the desired LineWidth of data to the output buffer.
1262 // Also make sure that we don't copy more than the string.
1263 // Also make sure that if there are linefeeds, we account for them.
1265 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1266 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1269 // Convert to CHAR16 value and show that we are done with this operation
1271 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1272 if (LineWidth
!= 0) {
1273 GetLineByWidthFinished
= TRUE
;
1276 if (Count2
== LineWidth
) {
1278 // Rewind the string from the maximum size until we see a space to break the line
1280 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1282 if (LineWidth
== 0) {
1290 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1293 // If currently pointing to a space, increment the index to the first non-space character
1296 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1300 *Index
= (UINT16
) (*Index
+ LineWidth
);
1309 Update display lines for a Menu Option.
1311 @param Selection The user's selection.
1312 @param MenuOption The MenuOption to be checked.
1316 UpdateOptionSkipLines (
1317 IN UI_MENU_SELECTION
*Selection
,
1318 IN UI_MENU_OPTION
*MenuOption
1325 CHAR16
*OutputString
;
1326 CHAR16
*OptionString
;
1329 OptionString
= NULL
;
1330 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1332 if (OptionString
!= NULL
) {
1333 Width
= (UINT16
) gOptionBlockWidth
;
1337 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1339 // If there is more string to process print on the next row and increment the Skip value
1341 if (StrLen (&OptionString
[Index
]) != 0) {
1344 // Since the Number of lines for this menu entry may or may not be reflected accurately
1345 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1346 // some testing to ensure we are keeping this in-sync.
1348 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1350 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1355 FreePool (OutputString
);
1361 if (OptionString
!= NULL
) {
1362 FreePool (OptionString
);
1368 Check whether this Menu Option could be highlighted.
1370 This is an internal function.
1372 @param MenuOption The MenuOption to be checked.
1374 @retval TRUE This Menu Option is selectable.
1375 @retval FALSE This Menu Option could not be selected.
1380 UI_MENU_OPTION
*MenuOption
1383 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1384 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1393 Determine if the menu is the last menu that can be selected.
1395 This is an internal function.
1397 @param Direction The scroll direction. False is down. True is up.
1398 @param CurrentPos The current focus.
1400 @return FALSE -- the menu isn't the last menu that can be selected.
1401 @return TRUE -- the menu is the last menu that can be selected.
1406 IN BOOLEAN Direction
,
1407 IN LIST_ENTRY
*CurrentPos
1412 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1414 if (Temp
== &gMenuOption
) {
1423 Move to next selectable statement.
1425 This is an internal function.
1427 @param Selection Menu selection.
1428 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1429 @param CurrentPosition Current position.
1430 @param GapToTop Gap position to top or bottom.
1432 @return The row distance from current MenuOption to next selectable MenuOption.
1436 MoveToNextStatement (
1437 IN UI_MENU_SELECTION
*Selection
,
1439 IN OUT LIST_ENTRY
**CurrentPosition
,
1445 UI_MENU_OPTION
*NextMenuOption
;
1446 UI_MENU_OPTION
*PreMenuOption
;
1449 Pos
= *CurrentPosition
;
1450 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1453 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1454 if (NextMenuOption
->Row
== 0) {
1455 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1458 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1460 // Current Position doesn't need to be caculated when go up.
1461 // Caculate distanct at first when go up
1463 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1464 NextMenuOption
= PreMenuOption
;
1467 Distance
+= NextMenuOption
->Skip
;
1469 if (IsSelectable (NextMenuOption
)) {
1472 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1481 // Caculate distanct at later when go down
1483 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1484 NextMenuOption
= PreMenuOption
;
1487 Distance
+= NextMenuOption
->Skip
;
1489 PreMenuOption
= NextMenuOption
;
1490 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1493 *CurrentPosition
= &NextMenuOption
->Link
;
1499 Adjust Data and Time position accordingly.
1500 Data format : [01/02/2004] [11:22:33]
1501 Line number : 0 0 1 0 0 1
1503 This is an internal function.
1505 @param DirectionUp the up or down direction. False is down. True is
1507 @param CurrentPosition Current position. On return: Point to the last
1508 Option (Year or Second) if up; Point to the first
1509 Option (Month or Hour) if down.
1511 @return Return line number to pad. It is possible that we stand on a zero-advance
1512 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1516 AdjustDateAndTimePosition (
1517 IN BOOLEAN DirectionUp
,
1518 IN OUT LIST_ENTRY
**CurrentPosition
1522 LIST_ENTRY
*NewPosition
;
1523 UI_MENU_OPTION
*MenuOption
;
1524 UINTN PadLineNumber
;
1527 NewPosition
= *CurrentPosition
;
1528 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1530 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1531 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1533 // Calculate the distance from current position to the last Date/Time MenuOption
1536 while (MenuOption
->Skip
== 0) {
1538 NewPosition
= NewPosition
->ForwardLink
;
1539 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1543 NewPosition
= *CurrentPosition
;
1546 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1547 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1548 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1549 // checking can be done.
1551 while (Count
++ < 2) {
1552 NewPosition
= NewPosition
->BackLink
;
1556 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1557 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1558 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1559 // checking can be done.
1561 while (Count
-- > 0) {
1562 NewPosition
= NewPosition
->ForwardLink
;
1566 *CurrentPosition
= NewPosition
;
1569 return PadLineNumber
;
1573 Find HII Handle in the HII database associated with given Device Path.
1575 If DevicePath is NULL, then ASSERT.
1577 @param DevicePath Device Path associated with the HII package list
1580 @retval Handle HII package list Handle associated with the Device
1582 @retval NULL Hii Package list handle is not found.
1587 DevicePathToHiiHandle (
1588 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1592 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1597 EFI_HANDLE DriverHandle
;
1598 EFI_HII_HANDLE
*HiiHandles
;
1599 EFI_HII_HANDLE HiiHandle
;
1601 ASSERT (DevicePath
!= NULL
);
1603 TmpDevicePath
= DevicePath
;
1605 // Locate Device Path Protocol handle buffer
1607 Status
= gBS
->LocateDevicePath (
1608 &gEfiDevicePathProtocolGuid
,
1612 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1617 // Retrieve all HII Handles from HII database
1619 BufferSize
= 0x1000;
1620 HiiHandles
= AllocatePool (BufferSize
);
1621 ASSERT (HiiHandles
!= NULL
);
1622 Status
= mHiiDatabase
->ListPackageLists (
1624 EFI_HII_PACKAGE_TYPE_ALL
,
1629 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1630 FreePool (HiiHandles
);
1631 HiiHandles
= AllocatePool (BufferSize
);
1632 ASSERT (HiiHandles
!= NULL
);
1634 Status
= mHiiDatabase
->ListPackageLists (
1636 EFI_HII_PACKAGE_TYPE_ALL
,
1643 if (EFI_ERROR (Status
)) {
1644 FreePool (HiiHandles
);
1649 // Search Hii Handle by Driver Handle
1652 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1653 for (Index
= 0; Index
< HandleCount
; Index
++) {
1654 Status
= mHiiDatabase
->GetPackageListHandle (
1659 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1660 HiiHandle
= HiiHandles
[Index
];
1665 FreePool (HiiHandles
);
1670 Find HII Handle in the HII database associated with given form set guid.
1672 If FormSetGuid is NULL, then ASSERT.
1674 @param ComparingGuid FormSet Guid associated with the HII package list
1677 @retval Handle HII package list Handle associated with the Device
1679 @retval NULL Hii Package list handle is not found.
1683 FormSetGuidToHiiHandle (
1684 EFI_GUID
*ComparingGuid
1687 EFI_HII_HANDLE
*HiiHandles
;
1689 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1693 UINT32 PackageListLength
;
1694 EFI_HII_PACKAGE_HEADER PackageHeader
;
1698 EFI_HII_HANDLE HiiHandle
;
1700 ASSERT (ComparingGuid
!= NULL
);
1704 // Get all the Hii handles
1706 HiiHandles
= HiiGetHiiHandles (NULL
);
1707 ASSERT (HiiHandles
!= NULL
);
1710 // Search for formset of each class type
1712 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1714 HiiPackageList
= NULL
;
1715 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1716 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1717 HiiPackageList
= AllocatePool (BufferSize
);
1718 ASSERT (HiiPackageList
!= NULL
);
1720 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1722 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1727 // Get Form package from this HII package List
1729 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1731 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1733 while (Offset
< PackageListLength
) {
1734 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1735 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1737 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1739 // Search FormSet in this Form Package
1741 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1742 while (Offset2
< PackageHeader
.Length
) {
1743 OpCodeData
= Package
+ Offset2
;
1745 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1747 // Try to compare against formset GUID
1749 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1750 HiiHandle
= HiiHandles
[Index
];
1755 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1758 if (HiiHandle
!= NULL
) {
1761 Offset
+= PackageHeader
.Length
;
1764 FreePool (HiiPackageList
);
1765 if (HiiHandle
!= NULL
) {
1770 FreePool (HiiHandles
);
1776 Process the goto op code, update the info in the selection structure.
1778 @param Statement The statement belong to goto op code.
1779 @param Selection The selection info.
1780 @param Repaint Whether need to repaint the menu.
1781 @param NewLine Whether need to create new line.
1783 @retval EFI_SUCCESS The menu process successfully.
1784 @return Other value if the process failed.
1788 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
1789 IN OUT UI_MENU_SELECTION
*Selection
,
1790 OUT BOOLEAN
*Repaint
,
1791 OUT BOOLEAN
*NewLine
1796 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1798 UINT8
*DevicePathBuffer
;
1801 FORM_BROWSER_FORM
*RefForm
;
1804 UI_MENU_LIST
*MenuList
;
1805 BOOLEAN UpdateFormInfo
;
1807 Status
= EFI_SUCCESS
;
1808 UpdateFormInfo
= TRUE
;
1810 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
1811 if (Selection
->Form
->ModalForm
) {
1815 // Goto another Hii Package list
1817 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1819 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
1820 if (StringPtr
== NULL
) {
1822 // No device path string not found, exit
1824 Selection
->Action
= UI_ACTION_EXIT
;
1825 Selection
->Statement
= NULL
;
1828 BufferSize
= StrLen (StringPtr
) / 2;
1829 DevicePath
= AllocatePool (BufferSize
);
1830 ASSERT (DevicePath
!= NULL
);
1833 // Convert from Device Path String to DevicePath Buffer in the reverse order.
1835 DevicePathBuffer
= (UINT8
*) DevicePath
;
1836 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
1837 TemStr
[0] = StringPtr
[Index
];
1838 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
1839 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
1841 // Invalid Hex Char as the tail.
1845 if ((Index
& 1) == 0) {
1846 DevicePathBuffer
[Index
/2] = DigitUint8
;
1848 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
1852 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
1853 if (Selection
->Handle
== NULL
) {
1855 // If target Hii Handle not found, exit
1857 Selection
->Action
= UI_ACTION_EXIT
;
1858 Selection
->Statement
= NULL
;
1862 FreePool (StringPtr
);
1863 FreePool (DevicePath
);
1865 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1866 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1867 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1868 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
1869 if (Selection
->Form
->ModalForm
) {
1873 // Goto another Formset, check for uncommitted data
1875 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1877 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
1878 if (Selection
->Handle
== NULL
) {
1880 // If target Hii Handle not found, exit
1882 Selection
->Action
= UI_ACTION_EXIT
;
1883 Selection
->Statement
= NULL
;
1887 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1888 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1889 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1890 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
1892 // Check whether target From is suppressed.
1894 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
1896 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
1897 Status
= EvaluateExpression (Selection
->FormSet
, RefForm
, RefForm
->SuppressExpression
);
1898 if (EFI_ERROR (Status
)) {
1902 if (RefForm
->SuppressExpression
->Result
.Value
.b
) {
1904 // Form is suppressed.
1907 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
1908 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
1909 if (Repaint
!= NULL
) {
1917 // Goto another form inside this formset,
1919 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
1921 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1922 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1923 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
1925 // Goto another Question
1927 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1929 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
1930 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
1932 if (Repaint
!= NULL
) {
1935 if (NewLine
!= NULL
) {
1939 UpdateFormInfo
= FALSE
;
1941 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
1942 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
1944 UpdateFormInfo
= FALSE
;
1947 if (UpdateFormInfo
) {
1949 // Link current form so that we can always go back when someone hits the ESC
1951 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
1952 if (MenuList
== NULL
&& Selection
->CurrentMenu
!= NULL
) {
1953 MenuList
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
1961 Display menu and wait for user to select one menu option, then return it.
1962 If AutoBoot is enabled, then if user doesn't select any option,
1963 after period of time, it will automatically return the first menu option.
1965 @param Selection Menu selection.
1967 @retval EFI_SUCESSS This function always return successfully for now.
1972 IN OUT UI_MENU_SELECTION
*Selection
1978 UINTN DistanceValue
;
1990 CHAR16
*OptionString
;
1991 CHAR16
*OutputString
;
1992 CHAR16
*FormattedString
;
1998 BOOLEAN InitializedFlag
;
2003 LIST_ENTRY
*TopOfScreen
;
2004 LIST_ENTRY
*SavedListEntry
;
2005 UI_MENU_OPTION
*MenuOption
;
2006 UI_MENU_OPTION
*NextMenuOption
;
2007 UI_MENU_OPTION
*SavedMenuOption
;
2008 UI_MENU_OPTION
*PreviousMenuOption
;
2009 UI_CONTROL_FLAG ControlFlag
;
2010 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2011 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2012 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2013 UI_SCREEN_OPERATION ScreenOperation
;
2014 UINT8 MinRefreshInterval
;
2016 FORM_BROWSER_STATEMENT
*Statement
;
2017 UI_MENU_LIST
*CurrentMenu
;
2018 UINTN ModalSkipColumn
;
2019 BROWSER_HOT_KEY
*HotKey
;
2021 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2023 Status
= EFI_SUCCESS
;
2024 FormattedString
= NULL
;
2025 OptionString
= NULL
;
2026 ScreenOperation
= UiNoOperation
;
2028 MinRefreshInterval
= 0;
2031 OutputString
= NULL
;
2036 MenuRefreshEntry
= gMenuRefreshHead
;
2038 NextMenuOption
= NULL
;
2039 PreviousMenuOption
= NULL
;
2040 SavedMenuOption
= NULL
;
2042 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2044 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2046 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2047 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2048 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2050 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2051 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2054 if (Selection
->Form
->ModalForm
) {
2055 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2057 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2060 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2062 Selection
->TopRow
= TopRow
;
2063 Selection
->BottomRow
= BottomRow
;
2064 Selection
->PromptCol
= Col
;
2065 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2066 Selection
->Statement
= NULL
;
2068 TopOfScreen
= gMenuOption
.ForwardLink
;
2073 // Find current Menu
2075 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2076 if (CurrentMenu
== NULL
) {
2078 // Current menu not found, add it to the menu tree
2080 CurrentMenu
= UiAddMenuList (NULL
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2082 ASSERT (CurrentMenu
!= NULL
);
2083 Selection
->CurrentMenu
= CurrentMenu
;
2085 if (Selection
->QuestionId
== 0) {
2087 // Highlight not specified, fetch it from cached menu
2089 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2090 Selection
->Sequence
= CurrentMenu
->Sequence
;
2094 // Init option as the current user's selection
2096 InitializedFlag
= TRUE
;
2097 NewPos
= gMenuOption
.ForwardLink
;
2099 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2100 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2102 ControlFlag
= CfInitialization
;
2103 Selection
->Action
= UI_ACTION_NONE
;
2105 switch (ControlFlag
) {
2106 case CfInitialization
:
2107 if (IsListEmpty (&gMenuOption
)) {
2108 ControlFlag
= CfReadKey
;
2110 ControlFlag
= CfCheckSelection
;
2114 case CfCheckSelection
:
2115 if (Selection
->Action
!= UI_ACTION_NONE
) {
2116 ControlFlag
= CfExit
;
2118 ControlFlag
= CfRepaint
;
2123 ControlFlag
= CfRefreshHighLight
;
2133 Temp
= (UINTN
) SkipValue
;
2134 Temp2
= (UINTN
) SkipValue
;
2136 if (Selection
->Form
->ModalForm
) {
2138 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2139 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2140 TopRow
- SCROLL_ARROW_HEIGHT
,
2141 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2142 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2146 LocalScreen
.LeftColumn
,
2147 LocalScreen
.RightColumn
,
2148 TopRow
- SCROLL_ARROW_HEIGHT
,
2149 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2150 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2153 UiFreeRefreshList ();
2154 MinRefreshInterval
= 0;
2156 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2157 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2158 MenuOption
->Row
= Row
;
2159 MenuOption
->Col
= Col
;
2160 if (Selection
->Form
->ModalForm
) {
2161 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2163 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2166 Statement
= MenuOption
->ThisTag
;
2167 if (Statement
->InSubtitle
) {
2168 MenuOption
->Col
+= SUBTITLE_INDENT
;
2171 if (MenuOption
->GrayOut
) {
2172 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2174 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2175 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2179 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2182 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2184 // Print Arrow for Goto button.
2187 MenuOption
->Col
- 2,
2190 GEOMETRICSHAPE_RIGHT_TRIANGLE
2194 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2195 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2196 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2199 // If there is more string to process print on the next row and increment the Skip value
2201 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2207 FreePool (OutputString
);
2216 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2217 if (EFI_ERROR (Status
)) {
2219 // Repaint to clear possible error prompt pop-up
2223 ControlFlag
= CfRepaint
;
2227 if (OptionString
!= NULL
) {
2228 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2230 // If leading spaces on OptionString - remove the spaces
2232 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
2233 MenuOption
->OptCol
++;
2236 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2237 OptionString
[Count
] = OptionString
[Index
];
2241 OptionString
[Count
] = CHAR_NULL
;
2244 Width
= (UINT16
) gOptionBlockWidth
;
2247 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2248 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2249 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2252 // If there is more string to process print on the next row and increment the Skip value
2254 if (StrLen (&OptionString
[Index
]) != 0) {
2258 // Since the Number of lines for this menu entry may or may not be reflected accurately
2259 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2260 // some testing to ensure we are keeping this in-sync.
2262 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2264 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2270 FreePool (OutputString
);
2279 FreePool (OptionString
);
2283 // If Question has refresh guid, register the op-code.
2285 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2286 if (gMenuEventGuidRefreshHead
== NULL
) {
2287 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2288 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2290 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2291 while (MenuUpdateEntry
->Next
!= NULL
) {
2292 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2294 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2295 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2297 ASSERT (MenuUpdateEntry
!= NULL
);
2298 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2299 ASSERT (!EFI_ERROR (Status
));
2300 MenuUpdateEntry
->MenuOption
= MenuOption
;
2301 MenuUpdateEntry
->Selection
= Selection
;
2302 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2303 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2304 if (MenuOption
->GrayOut
) {
2305 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2307 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2312 // If Question request refresh, register the op-code
2314 if (Statement
->RefreshInterval
!= 0) {
2316 // Menu will be refreshed at minimal interval of all Questions
2317 // which have refresh request
2319 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2320 MinRefreshInterval
= Statement
->RefreshInterval
;
2323 if (gMenuRefreshHead
== NULL
) {
2324 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2325 gMenuRefreshHead
= MenuRefreshEntry
;
2327 MenuRefreshEntry
= gMenuRefreshHead
;
2328 while (MenuRefreshEntry
->Next
!= NULL
) {
2329 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2331 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2332 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2334 ASSERT (MenuRefreshEntry
!= NULL
);
2335 MenuRefreshEntry
->MenuOption
= MenuOption
;
2336 MenuRefreshEntry
->Selection
= Selection
;
2337 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2338 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2339 if (MenuOption
->GrayOut
) {
2340 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2342 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2347 // If this is a text op with secondary text information
2349 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2350 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2352 Width
= (UINT16
) gOptionBlockWidth
;
2355 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
2356 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2357 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2360 // If there is more string to process print on the next row and increment the Skip value
2362 if (StrLen (&StringPtr
[Index
]) != 0) {
2366 // Since the Number of lines for this menu entry may or may not be reflected accurately
2367 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2368 // some testing to ensure we are keeping this in-sync.
2370 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2372 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2378 FreePool (OutputString
);
2385 FreePool (StringPtr
);
2387 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2390 // Need to handle the bottom of the display
2392 if (MenuOption
->Skip
> 1) {
2393 Row
+= MenuOption
->Skip
- SkipValue
;
2396 Row
+= MenuOption
->Skip
;
2399 if (Row
> BottomRow
) {
2400 if (!ValueIsScroll (FALSE
, Link
)) {
2404 Row
= BottomRow
+ 1;
2409 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2414 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2416 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2417 TopRow
- SCROLL_ARROW_HEIGHT
,
2421 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2425 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2427 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2428 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2432 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2439 case CfRefreshHighLight
:
2441 // MenuOption: Last menu option that need to remove hilight
2442 // MenuOption is set to NULL in Repaint
2443 // NewPos: Current menu option that need to hilight
2445 ControlFlag
= CfUpdateHelpString
;
2446 if (InitializedFlag
) {
2447 InitializedFlag
= FALSE
;
2448 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2452 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2453 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2455 SavedValue
= Repaint
;
2458 if (Selection
->QuestionId
!= 0) {
2459 NewPos
= gMenuOption
.ForwardLink
;
2460 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2462 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2463 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2464 NewPos
->ForwardLink
!= &gMenuOption
) {
2465 NewPos
= NewPos
->ForwardLink
;
2466 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2468 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2470 // Target Question found, find its MenuOption
2474 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2475 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2476 Index
+= SavedMenuOption
->Skip
;
2477 if (Link
== TopOfScreen
) {
2478 Index
-= OldSkipValue
;
2480 Link
= Link
->ForwardLink
;
2482 if (NewPos
== Link
) {
2483 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2486 if (Link
!= NewPos
|| Index
> BottomRow
|| (Link
== NewPos
&& SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
)) {
2488 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2490 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2492 // SavedMenuOption->Row == 0 means the menu not show yet.
2494 if (SavedMenuOption
->Row
== 0) {
2495 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2499 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2500 Link
= Link
->BackLink
;
2501 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2502 if (SavedMenuOption
->Row
== 0) {
2503 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2505 Index
+= SavedMenuOption
->Skip
;
2508 SkipValue
= Index
- BottomRow
- 1;
2509 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2511 OldSkipValue
= SkipValue
;
2514 TopOfScreen
= Link
->ForwardLink
;
2519 ControlFlag
= CfRepaint
;
2524 // Target Question not found, highlight the default menu option
2526 NewPos
= TopOfScreen
;
2529 Selection
->QuestionId
= 0;
2532 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2533 if (MenuOption
!= NULL
) {
2535 // Remove highlight on last Menu Option
2537 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2538 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2539 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2540 if (OptionString
!= NULL
) {
2541 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2542 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2545 // If leading spaces on OptionString - remove the spaces
2547 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2550 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2551 OptionString
[Count
] = OptionString
[Index
];
2555 OptionString
[Count
] = CHAR_NULL
;
2558 Width
= (UINT16
) gOptionBlockWidth
;
2559 OriginalRow
= MenuOption
->Row
;
2561 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2562 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2563 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2566 // If there is more string to process print on the next row and increment the Skip value
2568 if (StrLen (&OptionString
[Index
]) != 0) {
2572 FreePool (OutputString
);
2575 MenuOption
->Row
= OriginalRow
;
2577 FreePool (OptionString
);
2580 if (MenuOption
->GrayOut
) {
2581 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2582 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2583 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2586 OriginalRow
= MenuOption
->Row
;
2587 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2589 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2590 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2591 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2594 // If there is more string to process print on the next row and increment the Skip value
2596 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2600 FreePool (OutputString
);
2603 MenuOption
->Row
= OriginalRow
;
2604 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2610 // This is the current selected statement
2612 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2613 Statement
= MenuOption
->ThisTag
;
2614 Selection
->Statement
= Statement
;
2615 if (!IsSelectable (MenuOption
)) {
2616 Repaint
= SavedValue
;
2617 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2622 // Record highlight for current menu
2624 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2625 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2628 // Set reverse attribute
2630 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2631 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2634 // Assuming that we have a refresh linked-list created, lets annotate the
2635 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2636 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2638 if (gMenuRefreshHead
!= NULL
) {
2639 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2640 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2641 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2643 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2645 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2646 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2651 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2652 if (OptionString
!= NULL
) {
2653 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2655 // If leading spaces on OptionString - remove the spaces
2657 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++)
2660 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
2661 OptionString
[Count
] = OptionString
[Index
];
2665 OptionString
[Count
] = CHAR_NULL
;
2667 Width
= (UINT16
) gOptionBlockWidth
;
2669 OriginalRow
= MenuOption
->Row
;
2671 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2672 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2673 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2676 // If there is more string to process print on the next row and increment the Skip value
2678 if (StrLen (&OptionString
[Index
]) != 0) {
2682 FreePool (OutputString
);
2685 MenuOption
->Row
= OriginalRow
;
2687 FreePool (OptionString
);
2690 OriginalRow
= MenuOption
->Row
;
2692 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2694 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2695 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2696 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2699 // If there is more string to process print on the next row and increment the Skip value
2701 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2705 FreePool (OutputString
);
2708 MenuOption
->Row
= OriginalRow
;
2713 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2716 // Clear reverse attribute
2718 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2721 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2722 // if we didn't break halfway when process CfRefreshHighLight.
2724 Repaint
= SavedValue
;
2727 case CfUpdateHelpString
:
2728 ControlFlag
= CfPrepareToReadKey
;
2729 if (Selection
->Form
->ModalForm
) {
2733 if (Repaint
|| NewLine
) {
2735 // Don't print anything if it is a NULL help token
2737 ASSERT(MenuOption
!= NULL
);
2738 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
2741 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2744 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2746 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2748 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2750 // Pad String with spaces to simulate a clearing of the previous line
2752 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2753 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2757 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2759 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2764 // Reset this flag every time we finish using it.
2770 case CfPrepareToReadKey
:
2771 ControlFlag
= CfReadKey
;
2772 ScreenOperation
= UiNoOperation
;
2776 ControlFlag
= CfScreenOperation
;
2779 // Wait for user's selection
2782 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2783 } while (Status
== EFI_TIMEOUT
);
2785 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2787 // IFR is updated in Callback of refresh opcode, re-parse it
2789 ControlFlag
= CfCheckSelection
;
2790 Selection
->Statement
= NULL
;
2794 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2796 // If we encounter error, continue to read another key in.
2798 if (EFI_ERROR (Status
)) {
2799 ControlFlag
= CfReadKey
;
2803 switch (Key
.UnicodeChar
) {
2804 case CHAR_CARRIAGE_RETURN
:
2805 ScreenOperation
= UiSelect
;
2810 // We will push the adjustment of these numeric values directly to the input handler
2811 // NOTE: we won't handle manual input numeric
2816 // If the screen has no menu items, and the user didn't select UiReset
2817 // ignore the selection and go back to reading keys.
2819 if(IsListEmpty (&gMenuOption
)) {
2820 ControlFlag
= CfReadKey
;
2824 ASSERT(MenuOption
!= NULL
);
2825 Statement
= MenuOption
->ThisTag
;
2826 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2827 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2828 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2830 if (Key
.UnicodeChar
== '+') {
2831 gDirection
= SCAN_RIGHT
;
2833 gDirection
= SCAN_LEFT
;
2835 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2836 if (EFI_ERROR (Status
)) {
2838 // Repaint to clear possible error prompt pop-up
2843 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2845 if (OptionString
!= NULL
) {
2846 FreePool (OptionString
);
2852 ScreenOperation
= UiUp
;
2857 ScreenOperation
= UiDown
;
2861 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2863 // If the screen has no menu items, and the user didn't select UiReset
2864 // ignore the selection and go back to reading keys.
2866 if(IsListEmpty (&gMenuOption
)) {
2867 ControlFlag
= CfReadKey
;
2871 ASSERT(MenuOption
!= NULL
);
2872 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
) {
2873 ScreenOperation
= UiSelect
;
2879 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
2880 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2881 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2886 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
2888 // ModalForm has no ESC key and Hot Key.
2890 ControlFlag
= CfReadKey
;
2891 } else if (Index
== mScanCodeNumber
) {
2893 // Check whether Key matches the registered hot key.
2896 if ((gBrowserSettingScope
== SystemLevel
) || (gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
2897 HotKey
= GetHotKeyFromRegisterList (&Key
);
2899 if (HotKey
!= NULL
) {
2900 ScreenOperation
= UiHotKey
;
2907 case CfScreenOperation
:
2908 if (ScreenOperation
!= UiReset
) {
2910 // If the screen has no menu items, and the user didn't select UiReset
2911 // ignore the selection and go back to reading keys.
2913 if (IsListEmpty (&gMenuOption
)) {
2914 ControlFlag
= CfReadKey
;
2920 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
2923 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
2924 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
2931 ControlFlag
= CfCheckSelection
;
2933 ASSERT(MenuOption
!= NULL
);
2934 Statement
= MenuOption
->ThisTag
;
2935 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
2940 // Keep highlight on current MenuOption
2942 Selection
->QuestionId
= Statement
->QuestionId
;
2944 switch (Statement
->Operand
) {
2945 case EFI_IFR_REF_OP
:
2946 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
2949 case EFI_IFR_ACTION_OP
:
2951 // Process the Config string <ConfigResp>
2953 Status
= ProcessQuestionConfig (Selection
, Statement
);
2955 if (EFI_ERROR (Status
)) {
2960 // The action button may change some Question value, so refresh the form
2962 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2965 case EFI_IFR_RESET_BUTTON_OP
:
2967 // Reset Question to default value specified by DefaultId
2969 ControlFlag
= CfUiDefault
;
2970 DefaultId
= Statement
->DefaultId
;
2975 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2977 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
2978 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2980 if (EFI_ERROR (Status
)) {
2983 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2985 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2988 if (OptionString
!= NULL
) {
2989 FreePool (OptionString
);
2997 // We come here when someone press ESC
2999 ControlFlag
= CfCheckSelection
;
3000 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3004 ControlFlag
= CfCheckSelection
;
3005 ASSERT(MenuOption
!= NULL
);
3006 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3007 if (MenuOption
->Sequence
!= 0) {
3009 // In the middle or tail of the Date/Time op-code set, go left.
3011 ASSERT(NewPos
!= NULL
);
3012 NewPos
= NewPos
->BackLink
;
3018 ControlFlag
= CfCheckSelection
;
3019 ASSERT(MenuOption
!= NULL
);
3020 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3021 if (MenuOption
->Sequence
!= 2) {
3023 // In the middle or tail of the Date/Time op-code set, go left.
3025 ASSERT(NewPos
!= NULL
);
3026 NewPos
= NewPos
->ForwardLink
;
3032 ControlFlag
= CfCheckSelection
;
3034 SavedListEntry
= NewPos
;
3036 ASSERT(NewPos
!= NULL
);
3038 // Adjust Date/Time position before we advance forward.
3040 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3041 if (NewPos
->BackLink
!= &gMenuOption
) {
3042 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3043 ASSERT (MenuOption
!= NULL
);
3045 NewPos
= NewPos
->BackLink
;
3047 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3048 if (PreviousMenuOption
->Row
== 0) {
3049 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3051 DistanceValue
= PreviousMenuOption
->Skip
;
3053 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3054 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3056 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3058 if (Difference
< 0) {
3060 // We hit the begining MenuOption that can be focused
3061 // so we simply scroll to the top.
3063 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3064 TopOfScreen
= gMenuOption
.ForwardLink
;
3068 // Scroll up to the last page when we have arrived at top page.
3070 NewPos
= &gMenuOption
;
3071 TopOfScreen
= &gMenuOption
;
3072 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3073 ScreenOperation
= UiPageUp
;
3074 ControlFlag
= CfScreenOperation
;
3077 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3079 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3081 TopOfScreen
= NewPos
;
3085 } else if (!IsSelectable (NextMenuOption
)) {
3087 // Continue to go up until scroll to next page or the selectable option is found.
3089 ScreenOperation
= UiUp
;
3090 ControlFlag
= CfScreenOperation
;
3094 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3096 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3097 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3098 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3099 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3102 // Scroll up to the last page.
3104 NewPos
= &gMenuOption
;
3105 TopOfScreen
= &gMenuOption
;
3106 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3107 ScreenOperation
= UiPageUp
;
3108 ControlFlag
= CfScreenOperation
;
3113 ControlFlag
= CfCheckSelection
;
3115 ASSERT(NewPos
!= NULL
);
3116 if (NewPos
->BackLink
== &gMenuOption
) {
3126 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3127 Link
= Link
->BackLink
;
3128 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3129 if (PreviousMenuOption
->Row
== 0) {
3130 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3132 if (Index
< PreviousMenuOption
->Skip
) {
3136 Index
= Index
- PreviousMenuOption
->Skip
;
3139 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3140 if (TopOfScreen
== &gMenuOption
) {
3141 TopOfScreen
= gMenuOption
.ForwardLink
;
3142 NewPos
= gMenuOption
.BackLink
;
3143 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3145 } else if (TopOfScreen
!= Link
) {
3148 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3151 // Finally we know that NewPos is the last MenuOption can be focused.
3155 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3158 if (Index
+ 1 < TopRow
) {
3160 // Back up the previous option.
3162 Link
= Link
->ForwardLink
;
3166 // Move to the option in Next page.
3168 if (TopOfScreen
== &gMenuOption
) {
3169 NewPos
= gMenuOption
.BackLink
;
3170 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3173 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3177 // There are more MenuOption needing scrolling up.
3184 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3185 // Don't do this when we are already in the first page.
3187 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3188 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3192 ControlFlag
= CfCheckSelection
;
3194 ASSERT (NewPos
!= NULL
);
3195 if (NewPos
->ForwardLink
== &gMenuOption
) {
3204 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3206 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
3207 Index
= Index
+ NextMenuOption
->Skip
;
3208 Link
= Link
->ForwardLink
;
3209 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3212 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
)) {
3214 // Finally we know that NewPos is the last MenuOption can be focused.
3217 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3219 if (Index
- 1 > BottomRow
) {
3221 // Back up the previous option.
3223 Link
= Link
->BackLink
;
3226 // There are more MenuOption needing scrolling down.
3231 // Move to the option in Next page.
3233 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3237 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3238 // Don't do this when we are already in the last page.
3241 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3242 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3246 ControlFlag
= CfCheckSelection
;
3248 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3249 // to be one that progresses to the next set of op-codes, we need to advance to the last
3250 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3251 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3252 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3253 // the Date/Time op-code.
3255 SavedListEntry
= NewPos
;
3256 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3258 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3259 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3261 NewPos
= NewPos
->ForwardLink
;
3264 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3265 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3267 // We hit the end of MenuOption that can be focused
3268 // so we simply scroll to the first page.
3270 if (Difference
< 0) {
3272 // Scroll to the first page.
3274 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3275 TopOfScreen
= gMenuOption
.ForwardLink
;
3279 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3281 NewPos
= gMenuOption
.ForwardLink
;
3282 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3285 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3287 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3288 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3292 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3295 // An option might be multi-line, so we need to reflect that data in the overall skip value
3297 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3298 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3300 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3301 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3302 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3303 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3309 // If we are going to scroll, update TopOfScreen
3311 if (Temp
> BottomRow
) {
3314 // Is the current top of screen a zero-advance op-code?
3315 // If so, keep moving forward till we hit a >0 advance op-code
3317 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3320 // If bottom op-code is more than one line or top op-code is more than one line
3322 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3324 // Is the bottom op-code greater than or equal in size to the top op-code?
3326 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3328 // Skip the top op-code
3330 TopOfScreen
= TopOfScreen
->ForwardLink
;
3331 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3333 OldSkipValue
= Difference
;
3335 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3338 // If we have a remainder, skip that many more op-codes until we drain the remainder
3340 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3342 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3344 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3345 TopOfScreen
= TopOfScreen
->ForwardLink
;
3346 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3349 // Since we will act on this op-code in the next routine, and increment the
3350 // SkipValue, set the skips to one less than what is required.
3352 SkipValue
= Difference
- 1;
3356 // Since we will act on this op-code in the next routine, and increment the
3357 // SkipValue, set the skips to one less than what is required.
3359 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3362 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3363 TopOfScreen
= TopOfScreen
->ForwardLink
;
3366 SkipValue
= OldSkipValue
;
3370 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3371 // Let's set a skip flag to smoothly scroll the top of the screen.
3373 if (SavedMenuOption
->Skip
> 1) {
3374 if (SavedMenuOption
== NextMenuOption
) {
3379 } else if (SavedMenuOption
->Skip
== 1) {
3383 TopOfScreen
= TopOfScreen
->ForwardLink
;
3385 } while (SavedMenuOption
->Skip
== 0);
3388 OldSkipValue
= SkipValue
;
3389 } else if (!IsSelectable (NextMenuOption
)) {
3391 // Continue to go down until scroll to next page or the selectable option is found.
3393 ScreenOperation
= UiDown
;
3394 ControlFlag
= CfScreenOperation
;
3397 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3399 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3403 // Scroll to the first page.
3405 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3406 TopOfScreen
= gMenuOption
.ForwardLink
;
3410 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3413 NewPos
= gMenuOption
.ForwardLink
;
3414 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3418 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3420 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3421 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3425 ControlFlag
= CfCheckSelection
;
3427 Status
= EFI_SUCCESS
;
3429 // Discard changes. After it, no NV flag is showed.
3431 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3432 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3433 if (!EFI_ERROR (Status
)) {
3434 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3435 Selection
->Statement
= NULL
;
3436 gResetRequired
= FALSE
;
3439 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3440 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3442 // Still show current page.
3444 Selection
->Action
= UI_ACTION_NONE
;
3452 // Reterieve default setting. After it. NV flag will be showed.
3454 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3455 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
);
3456 if (!EFI_ERROR (Status
)) {
3457 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3458 Selection
->Statement
= NULL
;
3459 gResetRequired
= TRUE
;
3462 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3463 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3465 // Still show current page.
3467 Selection
->Action
= UI_ACTION_NONE
;
3475 // Save changes. After it, no NV flag is showed.
3477 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3478 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3479 if (!EFI_ERROR (Status
)) {
3480 ASSERT(MenuOption
!= NULL
);
3481 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3482 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3485 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3486 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3488 // Still show current page.
3490 Selection
->Action
= UI_ACTION_NONE
;
3498 // Set Reset required Flag
3500 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3501 gResetRequired
= TRUE
;
3507 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3509 // Form Exit without saving, Similar to ESC Key.
3510 // FormSet Exit without saving, Exit SendForm.
3511 // System Exit without saving, CallExitHandler and Exit SendForm.
3513 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3514 if (gBrowserSettingScope
== FormLevel
) {
3515 ControlFlag
= CfUiReset
;
3516 } else if (gBrowserSettingScope
== FormSetLevel
) {
3517 Selection
->Action
= UI_ACTION_EXIT
;
3518 } else if (gBrowserSettingScope
== SystemLevel
) {
3519 if (ExitHandlerFunction
!= NULL
) {
3520 ExitHandlerFunction ();
3522 Selection
->Action
= UI_ACTION_EXIT
;
3524 Selection
->Statement
= NULL
;
3529 ControlFlag
= CfCheckSelection
;
3531 // Reset to default value for all forms in the whole system.
3533 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
);
3535 if (!EFI_ERROR (Status
)) {
3536 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3537 Selection
->Statement
= NULL
;
3538 gResetRequired
= TRUE
;
3542 case CfUiNoOperation
:
3543 ControlFlag
= CfCheckSelection
;
3547 UiFreeRefreshList ();
3549 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3550 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3551 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3552 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");