2 Utility functions for User Interface functions.
4 Copyright (c) 2004 - 2012, 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
;
328 Process option string for date/time opcode.
330 @param MenuOption Menu option point to date/time.
331 @param OptionString Option string input for process.
332 @param AddOptCol Whether need to update MenuOption->OptCol.
336 ProcessStringForDateTime (
337 UI_MENU_OPTION
*MenuOption
,
338 CHAR16
*OptionString
,
344 FORM_BROWSER_STATEMENT
*Statement
;
346 ASSERT (MenuOption
!= NULL
&& OptionString
!= NULL
);
348 Statement
= MenuOption
->ThisTag
;
351 // If leading spaces on OptionString - remove the spaces
353 for (Index
= 0; OptionString
[Index
] == L
' '; Index
++) {
355 // Base on the blockspace to get the option column info.
358 MenuOption
->OptCol
++;
362 for (Count
= 0; OptionString
[Index
] != CHAR_NULL
; Index
++) {
363 OptionString
[Count
] = OptionString
[Index
];
366 OptionString
[Count
] = CHAR_NULL
;
369 // Enable to suppress field in the opcode base on the flag.
371 if (Statement
->Operand
== EFI_IFR_DATE_OP
) {
373 // OptionString format is: <**: **: ****>
377 if ((Statement
->Flags
& EFI_QF_DATE_MONTH_SUPPRESS
) && (MenuOption
->Sequence
== 0)) {
379 // At this point, only "<**:" in the optionstring.
380 // Clean the day's ** field, after clean, the format is "< :"
382 SetUnicodeMem (&OptionString
[1], 2, L
' ');
383 } else if ((Statement
->Flags
& EFI_QF_DATE_DAY_SUPPRESS
) && (MenuOption
->Sequence
== 1)) {
385 // At this point, only "**:" in the optionstring.
386 // Clean the month's "**" field, after clean, the format is " :"
388 SetUnicodeMem (&OptionString
[0], 2, L
' ');
389 } else if ((Statement
->Flags
& EFI_QF_DATE_YEAR_SUPPRESS
) && (MenuOption
->Sequence
== 2)) {
391 // At this point, only "****>" in the optionstring.
392 // Clean the year's "****" field, after clean, the format is " >"
394 SetUnicodeMem (&OptionString
[0], 4, L
' ');
396 } else if (Statement
->Operand
== EFI_IFR_TIME_OP
) {
398 // OptionString format is: <**: **: **>
399 // |hour|minute|second|
402 if ((Statement
->Flags
& QF_TIME_HOUR_SUPPRESS
) && (MenuOption
->Sequence
== 0)) {
404 // At this point, only "<**:" in the optionstring.
405 // Clean the hour's ** field, after clean, the format is "< :"
407 SetUnicodeMem (&OptionString
[1], 2, L
' ');
408 } else if ((Statement
->Flags
& QF_TIME_MINUTE_SUPPRESS
) && (MenuOption
->Sequence
== 1)) {
410 // At this point, only "**:" in the optionstring.
411 // Clean the minute's "**" field, after clean, the format is " :"
413 SetUnicodeMem (&OptionString
[0], 2, L
' ');
414 } else if ((Statement
->Flags
& QF_TIME_SECOND_SUPPRESS
) && (MenuOption
->Sequence
== 2)) {
416 // At this point, only "**>" in the optionstring.
417 // Clean the second's "**" field, after clean, the format is " >"
419 SetUnicodeMem (&OptionString
[0], 2, L
' ');
427 @param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
431 IN MENU_REFRESH_ENTRY
*MenuRefreshEntry
434 CHAR16
*OptionString
;
436 UI_MENU_SELECTION
*Selection
;
437 FORM_BROWSER_STATEMENT
*Question
;
439 Selection
= MenuRefreshEntry
->Selection
;
440 Question
= MenuRefreshEntry
->MenuOption
->ThisTag
;
442 Status
= GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, FALSE
);
443 if (EFI_ERROR (Status
)) {
448 ProcessOptions (Selection
, MenuRefreshEntry
->MenuOption
, FALSE
, &OptionString
);
450 if (OptionString
!= NULL
) {
452 // If old Text is longer than new string, need to clean the old string before paint the newer.
453 // This option is no need for time/date opcode, because time/data opcode has fixed string length.
455 if ((MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_DATE_OP
) &&
456 (MenuRefreshEntry
->MenuOption
->ThisTag
->Operand
!= EFI_IFR_TIME_OP
)) {
458 MenuRefreshEntry
->CurrentColumn
,
459 MenuRefreshEntry
->CurrentColumn
+ gOptionBlockWidth
- 1,
460 MenuRefreshEntry
->CurrentRow
,
461 MenuRefreshEntry
->CurrentRow
,
462 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
466 gST
->ConOut
->SetAttribute (gST
->ConOut
, MenuRefreshEntry
->CurrentAttribute
);
467 ProcessStringForDateTime(MenuRefreshEntry
->MenuOption
, OptionString
, FALSE
);
468 PrintStringAt (MenuRefreshEntry
->CurrentColumn
, MenuRefreshEntry
->CurrentRow
, OptionString
);
469 FreePool (OptionString
);
473 // Question value may be changed, need invoke its Callback()
475 Status
= ProcessCallBackFunction (Selection
, Question
, EFI_BROWSER_ACTION_CHANGING
, FALSE
);
481 Refresh the question which has refresh guid event attribute.
483 @param Event The event which has this function related.
484 @param Context The input context info related to this event or the status code return to the caller.
488 RefreshQuestionNotify(
493 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
494 UI_MENU_SELECTION
*Selection
;
497 // Reset FormPackage update flag
499 mHiiPackageListUpdated
= FALSE
;
501 MenuRefreshEntry
= (MENU_REFRESH_ENTRY
*)Context
;
502 ASSERT (MenuRefreshEntry
!= NULL
);
503 Selection
= MenuRefreshEntry
->Selection
;
505 RefreshQuestion (MenuRefreshEntry
);
507 if (mHiiPackageListUpdated
) {
509 // Package list is updated, force to reparse IFR binary of target Formset
511 mHiiPackageListUpdated
= FALSE
;
512 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
526 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
528 UI_MENU_SELECTION
*Selection
;
530 if (gMenuRefreshHead
!= NULL
) {
532 // call from refresh interval process.
534 MenuRefreshEntry
= gMenuRefreshHead
;
535 Selection
= MenuRefreshEntry
->Selection
;
537 // Reset FormPackage update flag
539 mHiiPackageListUpdated
= FALSE
;
542 Status
= RefreshQuestion (MenuRefreshEntry
);
543 if (EFI_ERROR (Status
)) {
547 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
549 } while (MenuRefreshEntry
!= NULL
);
551 if (mHiiPackageListUpdated
) {
553 // Package list is updated, force to reparse IFR binary of target Formset
555 mHiiPackageListUpdated
= FALSE
;
556 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
566 Wait for a given event to fire, or for an optional timeout to expire.
568 @param Event The event to wait for
569 @param Timeout An optional timeout value in 100 ns units.
570 @param RefreshInterval Menu refresh interval (in seconds).
572 @retval EFI_SUCCESS Event fired before Timeout expired.
573 @retval EFI_TIME_OUT Timout expired before Event fired.
577 UiWaitForSingleEvent (
579 IN UINT64 Timeout
, OPTIONAL
580 IN UINT8 RefreshInterval OPTIONAL
585 EFI_EVENT TimerEvent
;
586 EFI_EVENT WaitList
[2];
590 // Create a timer event
592 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
593 if (!EFI_ERROR (Status
)) {
595 // Set the timer event
604 // Wait for the original event or the timer
607 WaitList
[1] = TimerEvent
;
608 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
609 gBS
->CloseEvent (TimerEvent
);
612 // If the timer expired, change the return to timed out
614 if (!EFI_ERROR (Status
) && Index
== 1) {
615 Status
= EFI_TIMEOUT
;
620 // Update screen every second
622 if (RefreshInterval
== 0) {
623 Timeout
= ONE_SECOND
;
625 Timeout
= RefreshInterval
* ONE_SECOND
;
629 Status
= gBS
->CreateEvent (EVT_TIMER
, 0, NULL
, NULL
, &TimerEvent
);
632 // Set the timer event
641 // Wait for the original event or the timer
644 WaitList
[1] = TimerEvent
;
645 Status
= gBS
->WaitForEvent (2, WaitList
, &Index
);
648 // If the timer expired, update anything that needs a refresh and keep waiting
650 if (!EFI_ERROR (Status
) && Index
== 1) {
651 Status
= EFI_TIMEOUT
;
652 if (RefreshInterval
!= 0) {
653 Status
= RefreshForm ();
657 gBS
->CloseEvent (TimerEvent
);
658 } while (Status
== EFI_TIMEOUT
);
666 Add one menu option by specified description and context.
668 @param String String description for this option.
669 @param Handle Hii handle for the package list.
670 @param Form The form this statement belong to.
671 @param Statement Statement of this Menu Option.
672 @param NumberOfLines Display lines for this Menu Option.
673 @param MenuItemCount The index for this Option in the Menu.
675 @retval Pointer Pointer to the added Menu Option.
681 IN EFI_HII_HANDLE Handle
,
682 IN FORM_BROWSER_FORM
*Form
,
683 IN FORM_BROWSER_STATEMENT
*Statement
,
684 IN UINT16 NumberOfLines
,
685 IN UINT16 MenuItemCount
688 UI_MENU_OPTION
*MenuOption
;
695 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
697 // Add three MenuOptions for Date/Time
698 // Data format : [01/02/2004] [11:22:33]
699 // Line number : 0 0 1 0 0 1
704 if (Statement
->Storage
== NULL
) {
706 // For RTC type of date/time, set default refresh interval to be 1 second
708 if (Statement
->RefreshInterval
== 0) {
709 Statement
->RefreshInterval
= 1;
714 for (Index
= 0; Index
< Count
; Index
++) {
715 MenuOption
= AllocateZeroPool (sizeof (UI_MENU_OPTION
));
718 MenuOption
->Signature
= UI_MENU_OPTION_SIGNATURE
;
719 MenuOption
->Description
= String
;
720 MenuOption
->Handle
= Handle
;
721 MenuOption
->ThisTag
= Statement
;
722 MenuOption
->EntryNumber
= MenuItemCount
;
726 // Override LineNumber for the MenuOption in Date/Time sequence
728 MenuOption
->Skip
= 1;
730 MenuOption
->Skip
= NumberOfLines
;
732 MenuOption
->Sequence
= Index
;
734 if (EvaluateExpressionList(Statement
->Expression
, FALSE
, NULL
, NULL
) == ExpressGrayOut
) {
735 MenuOption
->GrayOut
= TRUE
;
737 MenuOption
->GrayOut
= FALSE
;
741 // If the form or the question has the lock attribute, deal same as grayout.
743 if (Form
->Locked
|| Statement
->Locked
) {
744 MenuOption
->GrayOut
= TRUE
;
747 switch (Statement
->Operand
) {
748 case EFI_IFR_ORDERED_LIST_OP
:
749 case EFI_IFR_ONE_OF_OP
:
750 case EFI_IFR_NUMERIC_OP
:
751 case EFI_IFR_TIME_OP
:
752 case EFI_IFR_DATE_OP
:
753 case EFI_IFR_CHECKBOX_OP
:
754 case EFI_IFR_PASSWORD_OP
:
755 case EFI_IFR_STRING_OP
:
757 // User could change the value of these items
759 MenuOption
->IsQuestion
= TRUE
;
762 case EFI_IFR_TEXT_OP
:
763 if (FeaturePcdGet (PcdBrowserGrayOutTextStatement
)) {
765 // Initializing GrayOut option as TRUE for Text setup options
766 // so that those options will be Gray in colour and un selectable.
768 MenuOption
->GrayOut
= TRUE
;
772 MenuOption
->IsQuestion
= FALSE
;
776 if ((Statement
->ValueExpression
!= NULL
) ||
777 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
778 MenuOption
->ReadOnly
= TRUE
;
781 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
789 Routine used to abstract a generic dialog interface and return the selected key or string
791 @param NumberOfLines The number of lines for the dialog box
792 @param HotKey Defines whether a single character is parsed
793 (TRUE) and returned in KeyValue or a string is
794 returned in StringBuffer. Two special characters
795 are considered when entering a string, a SCAN_ESC
796 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
797 string input and returns
798 @param MaximumStringSize The maximum size in bytes of a typed in string
799 (each character is a CHAR16) and the minimum
800 string returned is two bytes
801 @param StringBuffer The passed in pointer to the buffer which will
802 hold the typed in string if HotKey is FALSE
803 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
804 @param ... A series of (quantity == NumberOfLines) text
805 strings which will be used to construct the dialog
808 @retval EFI_SUCCESS Displayed dialog and received user interaction
809 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
810 (StringBuffer == NULL) && (HotKey == FALSE))
811 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
817 IN UINTN NumberOfLines
,
819 IN UINTN MaximumStringSize
,
820 OUT CHAR16
*StringBuffer
,
821 OUT EFI_INPUT_KEY
*KeyValue
,
830 CHAR16
*BufferedString
;
837 BOOLEAN SelectionComplete
;
839 UINTN CurrentAttribute
;
840 UINTN DimensionsWidth
;
841 UINTN DimensionsHeight
;
843 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
844 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
846 SelectionComplete
= FALSE
;
848 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
849 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
850 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
853 ASSERT (BufferedString
);
855 VA_START (Marker
, KeyValue
);
858 // Zero the outgoing buffer
860 ZeroMem (StringBuffer
, MaximumStringSize
);
863 if (KeyValue
== NULL
) {
864 return EFI_INVALID_PARAMETER
;
867 if (StringBuffer
== NULL
) {
868 return EFI_INVALID_PARAMETER
;
874 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
879 // Determine the largest string in the dialog box
880 // Notice we are starting with 1 since String is the first string
882 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
883 StackString
= VA_ARG (Marker
, CHAR16
*);
885 if (StackString
[0] == L
' ') {
886 InputOffset
= Count
+ 1;
889 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
891 // Size of the string visually and subtract the width by one for the null-terminator
893 LargestString
= (GetStringWidth (StackString
) / 2);
898 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
899 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
906 VA_START (Marker
, KeyValue
);
907 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
911 // Take the first key typed and report it back?
914 Status
= WaitForKeyStroke (&Key
);
915 ASSERT_EFI_ERROR (Status
);
916 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
920 Status
= WaitForKeyStroke (&Key
);
922 switch (Key
.UnicodeChar
) {
924 switch (Key
.ScanCode
) {
926 FreePool (TempString
);
927 FreePool (BufferedString
);
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
929 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
930 return EFI_DEVICE_ERROR
;
938 case CHAR_CARRIAGE_RETURN
:
939 SelectionComplete
= TRUE
;
940 FreePool (TempString
);
941 FreePool (BufferedString
);
942 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
943 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
948 if (StringBuffer
[0] != CHAR_NULL
) {
949 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
950 TempString
[Index
] = StringBuffer
[Index
];
953 // Effectively truncate string by 1 character
955 TempString
[Index
- 1] = CHAR_NULL
;
956 StrCpy (StringBuffer
, TempString
);
961 // If it is the beginning of the string, don't worry about checking maximum limits
963 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
964 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
965 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
966 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
967 KeyPad
[0] = Key
.UnicodeChar
;
968 KeyPad
[1] = CHAR_NULL
;
969 StrCat (StringBuffer
, KeyPad
);
970 StrCat (TempString
, KeyPad
);
973 // If the width of the input string is now larger than the screen, we nee to
974 // adjust the index to start printing portions of the string
976 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
978 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
980 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
981 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
986 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
987 BufferedString
[Count
] = StringBuffer
[Index
];
990 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
993 } while (!SelectionComplete
);
996 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
997 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1002 Draw a pop up windows based on the dimension, number of lines and
1005 @param RequestedWidth The width of the pop-up.
1006 @param NumberOfLines The number of lines.
1007 @param Marker The variable argument list for the list of string to be printed.
1012 IN UINTN RequestedWidth
,
1013 IN UINTN NumberOfLines
,
1025 UINTN DimensionsWidth
;
1026 UINTN DimensionsHeight
;
1028 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
1029 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
1031 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1033 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
1034 RequestedWidth
= DimensionsWidth
- 2;
1038 // Subtract the PopUp width from total Columns, allow for one space extra on
1039 // each end plus a border.
1041 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
1042 End
= Start
+ RequestedWidth
+ 1;
1044 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
1045 Bottom
= Top
+ NumberOfLines
+ 2;
1047 Character
= BOXDRAW_DOWN_RIGHT
;
1048 PrintCharAt (Start
, Top
, Character
);
1049 Character
= BOXDRAW_HORIZONTAL
;
1050 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1051 PrintChar (Character
);
1054 Character
= BOXDRAW_DOWN_LEFT
;
1055 PrintChar (Character
);
1056 Character
= BOXDRAW_VERTICAL
;
1059 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
1060 String
= VA_ARG (Marker
, CHAR16
*);
1063 // This will clear the background of the line - we never know who might have been
1064 // here before us. This differs from the next clear in that it used the non-reverse
1065 // video for normal printing.
1067 if (GetStringWidth (String
) / 2 > 1) {
1068 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1072 // Passing in a space results in the assumption that this is where typing will occur
1074 if (String
[0] == L
' ') {
1075 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
1079 // Passing in a NULL results in a blank space
1081 if (String
[0] == CHAR_NULL
) {
1082 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1086 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
1090 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1091 PrintCharAt (Start
, Index
+ 1, Character
);
1092 PrintCharAt (End
- 1, Index
+ 1, Character
);
1095 Character
= BOXDRAW_UP_RIGHT
;
1096 PrintCharAt (Start
, Bottom
- 1, Character
);
1097 Character
= BOXDRAW_HORIZONTAL
;
1098 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1099 PrintChar (Character
);
1102 Character
= BOXDRAW_UP_LEFT
;
1103 PrintChar (Character
);
1107 Draw a pop up windows based on the dimension, number of lines and
1110 @param RequestedWidth The width of the pop-up.
1111 @param NumberOfLines The number of lines.
1112 @param ... A series of text strings that displayed in the pop-up.
1117 CreateMultiStringPopUp (
1118 IN UINTN RequestedWidth
,
1119 IN UINTN NumberOfLines
,
1125 VA_START (Marker
, NumberOfLines
);
1127 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1134 Update status bar on the bottom of menu.
1136 @param Selection Current Selction info.
1137 @param MessageType The type of message to be shown.
1138 @param Flags The flags in Question header.
1139 @param State Set or clear.
1144 IN UI_MENU_SELECTION
*Selection
,
1145 IN UINTN MessageType
,
1151 CHAR16
*NvUpdateMessage
;
1152 CHAR16
*InputErrorMessage
;
1154 FORM_BROWSER_FORMSET
*LocalFormSet
;
1155 FORM_BROWSER_STATEMENT
*Question
;
1157 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1158 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1160 switch (MessageType
) {
1163 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1165 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1166 gScreenDimensions
.BottomRow
- 1,
1171 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1172 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1173 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1176 mInputError
= FALSE
;
1180 case NV_UPDATE_REQUIRED
:
1182 // Global setting support. Show configuration change on every form.
1185 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1187 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1188 Question
= Selection
->Statement
;
1189 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1191 // Update only for Question value that need to be saved into Storage.
1193 Selection
->Form
->NvUpdateRequired
= TRUE
;
1197 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1198 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1200 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1201 gScreenDimensions
.BottomRow
- 1,
1206 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1207 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1209 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1210 gScreenDimensions
.BottomRow
- 1,
1217 case REFRESH_STATUS_BAR
:
1219 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1222 switch (gBrowserSettingScope
) {
1225 // Check the maintain list to see whether there is any change.
1227 Link
= GetFirstNode (&gBrowserFormSetList
);
1228 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1229 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1230 if (IsNvUpdateRequired(LocalFormSet
)) {
1231 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1234 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1239 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1250 FreePool (InputErrorMessage
);
1251 FreePool (NvUpdateMessage
);
1257 Get the supported width for a particular op-code
1259 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1260 @param Handle The handle in the HII database being used
1262 @return Returns the number of CHAR16 characters that is support.
1267 IN FORM_BROWSER_STATEMENT
*Statement
,
1268 IN EFI_HII_HANDLE Handle
1278 // See if the second text parameter is really NULL
1280 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1281 String
= GetToken (Statement
->TextTwo
, Handle
);
1282 Size
= StrLen (String
);
1286 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1287 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1288 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1289 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1290 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1292 // Allow a wide display if text op-code and no secondary text op-code
1294 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1296 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1298 Width
= (UINT16
) gPromptBlockWidth
;
1301 if (Statement
->InSubtitle
) {
1302 Width
-= SUBTITLE_INDENT
;
1305 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1309 Will copy LineWidth amount of a string in the OutputString buffer and return the
1310 number of CHAR16 characters that were copied into the OutputString buffer.
1312 @param InputString String description for this option.
1313 @param LineWidth Width of the desired string to extract in CHAR16
1315 @param Index Where in InputString to start the copy process
1316 @param OutputString Buffer to copy the string into
1318 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1323 IN CHAR16
*InputString
,
1324 IN UINT16 LineWidth
,
1325 IN OUT UINTN
*Index
,
1326 OUT CHAR16
**OutputString
1332 if (GetLineByWidthFinished
) {
1333 GetLineByWidthFinished
= FALSE
;
1340 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1343 // Ensure we have got a valid buffer
1345 if (*OutputString
!= NULL
) {
1348 //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.
1349 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1351 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1352 *Index
= *Index
+ 2;
1356 // Fast-forward the string and see if there is a carriage-return in the string
1358 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1362 // Copy the desired LineWidth of data to the output buffer.
1363 // Also make sure that we don't copy more than the string.
1364 // Also make sure that if there are linefeeds, we account for them.
1366 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1367 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1370 // Convert to CHAR16 value and show that we are done with this operation
1372 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1373 if (LineWidth
!= 0) {
1374 GetLineByWidthFinished
= TRUE
;
1377 if (Count2
== LineWidth
) {
1379 // Rewind the string from the maximum size until we see a space to break the line
1381 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1383 if (LineWidth
== 0) {
1391 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1394 // If currently pointing to a space, increment the index to the first non-space character
1397 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1401 *Index
= (UINT16
) (*Index
+ LineWidth
);
1410 Update display lines for a Menu Option.
1412 @param Selection The user's selection.
1413 @param MenuOption The MenuOption to be checked.
1417 UpdateOptionSkipLines (
1418 IN UI_MENU_SELECTION
*Selection
,
1419 IN UI_MENU_OPTION
*MenuOption
1426 CHAR16
*OutputString
;
1427 CHAR16
*OptionString
;
1430 OptionString
= NULL
;
1431 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1433 if (OptionString
!= NULL
) {
1434 Width
= (UINT16
) gOptionBlockWidth
;
1438 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1440 // If there is more string to process print on the next row and increment the Skip value
1442 if (StrLen (&OptionString
[Index
]) != 0) {
1445 // Since the Number of lines for this menu entry may or may not be reflected accurately
1446 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1447 // some testing to ensure we are keeping this in-sync.
1449 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1451 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1456 FreePool (OutputString
);
1462 if (OptionString
!= NULL
) {
1463 FreePool (OptionString
);
1469 Check whether this Menu Option could be highlighted.
1471 This is an internal function.
1473 @param MenuOption The MenuOption to be checked.
1475 @retval TRUE This Menu Option is selectable.
1476 @retval FALSE This Menu Option could not be selected.
1481 UI_MENU_OPTION
*MenuOption
1484 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1485 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1494 Determine if the menu is the last menu that can be selected.
1496 This is an internal function.
1498 @param Direction The scroll direction. False is down. True is up.
1499 @param CurrentPos The current focus.
1501 @return FALSE -- the menu isn't the last menu that can be selected.
1502 @return TRUE -- the menu is the last menu that can be selected.
1507 IN BOOLEAN Direction
,
1508 IN LIST_ENTRY
*CurrentPos
1513 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1515 if (Temp
== &gMenuOption
) {
1524 Move to next selectable statement.
1526 This is an internal function.
1528 @param Selection Menu selection.
1529 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1530 @param CurrentPosition Current position.
1531 @param GapToTop Gap position to top or bottom.
1533 @return The row distance from current MenuOption to next selectable MenuOption.
1537 MoveToNextStatement (
1538 IN UI_MENU_SELECTION
*Selection
,
1540 IN OUT LIST_ENTRY
**CurrentPosition
,
1546 UI_MENU_OPTION
*NextMenuOption
;
1547 UI_MENU_OPTION
*PreMenuOption
;
1550 Pos
= *CurrentPosition
;
1551 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1554 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1555 if (NextMenuOption
->Row
== 0) {
1556 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1559 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1561 // Current Position doesn't need to be caculated when go up.
1562 // Caculate distanct at first when go up
1564 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1565 NextMenuOption
= PreMenuOption
;
1568 Distance
+= NextMenuOption
->Skip
;
1570 if (IsSelectable (NextMenuOption
)) {
1573 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1582 // Caculate distanct at later when go down
1584 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1585 NextMenuOption
= PreMenuOption
;
1588 Distance
+= NextMenuOption
->Skip
;
1590 PreMenuOption
= NextMenuOption
;
1591 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1594 *CurrentPosition
= &NextMenuOption
->Link
;
1600 Adjust Data and Time position accordingly.
1601 Data format : [01/02/2004] [11:22:33]
1602 Line number : 0 0 1 0 0 1
1604 This is an internal function.
1606 @param DirectionUp the up or down direction. False is down. True is
1608 @param CurrentPosition Current position. On return: Point to the last
1609 Option (Year or Second) if up; Point to the first
1610 Option (Month or Hour) if down.
1612 @return Return line number to pad. It is possible that we stand on a zero-advance
1613 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1617 AdjustDateAndTimePosition (
1618 IN BOOLEAN DirectionUp
,
1619 IN OUT LIST_ENTRY
**CurrentPosition
1623 LIST_ENTRY
*NewPosition
;
1624 UI_MENU_OPTION
*MenuOption
;
1625 UINTN PadLineNumber
;
1628 NewPosition
= *CurrentPosition
;
1629 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1631 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1632 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1634 // Calculate the distance from current position to the last Date/Time MenuOption
1637 while (MenuOption
->Skip
== 0) {
1639 NewPosition
= NewPosition
->ForwardLink
;
1640 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1644 NewPosition
= *CurrentPosition
;
1647 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1648 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1649 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1650 // checking can be done.
1652 while (Count
++ < 2) {
1653 NewPosition
= NewPosition
->BackLink
;
1657 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1658 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1659 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1660 // checking can be done.
1662 while (Count
-- > 0) {
1663 NewPosition
= NewPosition
->ForwardLink
;
1667 *CurrentPosition
= NewPosition
;
1670 return PadLineNumber
;
1674 Find HII Handle in the HII database associated with given Device Path.
1676 If DevicePath is NULL, then ASSERT.
1678 @param DevicePath Device Path associated with the HII package list
1681 @retval Handle HII package list Handle associated with the Device
1683 @retval NULL Hii Package list handle is not found.
1688 DevicePathToHiiHandle (
1689 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1693 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1698 EFI_HANDLE DriverHandle
;
1699 EFI_HII_HANDLE
*HiiHandles
;
1700 EFI_HII_HANDLE HiiHandle
;
1702 ASSERT (DevicePath
!= NULL
);
1704 TmpDevicePath
= DevicePath
;
1706 // Locate Device Path Protocol handle buffer
1708 Status
= gBS
->LocateDevicePath (
1709 &gEfiDevicePathProtocolGuid
,
1713 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1718 // Retrieve all HII Handles from HII database
1720 BufferSize
= 0x1000;
1721 HiiHandles
= AllocatePool (BufferSize
);
1722 ASSERT (HiiHandles
!= NULL
);
1723 Status
= mHiiDatabase
->ListPackageLists (
1725 EFI_HII_PACKAGE_TYPE_ALL
,
1730 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1731 FreePool (HiiHandles
);
1732 HiiHandles
= AllocatePool (BufferSize
);
1733 ASSERT (HiiHandles
!= NULL
);
1735 Status
= mHiiDatabase
->ListPackageLists (
1737 EFI_HII_PACKAGE_TYPE_ALL
,
1744 if (EFI_ERROR (Status
)) {
1745 FreePool (HiiHandles
);
1750 // Search Hii Handle by Driver Handle
1753 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1754 for (Index
= 0; Index
< HandleCount
; Index
++) {
1755 Status
= mHiiDatabase
->GetPackageListHandle (
1760 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1761 HiiHandle
= HiiHandles
[Index
];
1766 FreePool (HiiHandles
);
1771 Find HII Handle in the HII database associated with given form set guid.
1773 If FormSetGuid is NULL, then ASSERT.
1775 @param ComparingGuid FormSet Guid associated with the HII package list
1778 @retval Handle HII package list Handle associated with the Device
1780 @retval NULL Hii Package list handle is not found.
1784 FormSetGuidToHiiHandle (
1785 EFI_GUID
*ComparingGuid
1788 EFI_HII_HANDLE
*HiiHandles
;
1790 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1794 UINT32 PackageListLength
;
1795 EFI_HII_PACKAGE_HEADER PackageHeader
;
1799 EFI_HII_HANDLE HiiHandle
;
1801 ASSERT (ComparingGuid
!= NULL
);
1805 // Get all the Hii handles
1807 HiiHandles
= HiiGetHiiHandles (NULL
);
1808 ASSERT (HiiHandles
!= NULL
);
1811 // Search for formset of each class type
1813 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1815 HiiPackageList
= NULL
;
1816 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1817 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1818 HiiPackageList
= AllocatePool (BufferSize
);
1819 ASSERT (HiiPackageList
!= NULL
);
1821 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1823 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1828 // Get Form package from this HII package List
1830 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1832 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1834 while (Offset
< PackageListLength
) {
1835 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1836 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1838 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1840 // Search FormSet in this Form Package
1842 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1843 while (Offset2
< PackageHeader
.Length
) {
1844 OpCodeData
= Package
+ Offset2
;
1846 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1848 // Try to compare against formset GUID
1850 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1851 HiiHandle
= HiiHandles
[Index
];
1856 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1859 if (HiiHandle
!= NULL
) {
1862 Offset
+= PackageHeader
.Length
;
1865 FreePool (HiiPackageList
);
1866 if (HiiHandle
!= NULL
) {
1871 FreePool (HiiHandles
);
1877 Process the goto op code, update the info in the selection structure.
1879 @param Statement The statement belong to goto op code.
1880 @param Selection The selection info.
1881 @param Repaint Whether need to repaint the menu.
1882 @param NewLine Whether need to create new line.
1884 @retval EFI_SUCCESS The menu process successfully.
1885 @return Other value if the process failed.
1889 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
1890 IN OUT UI_MENU_SELECTION
*Selection
,
1891 OUT BOOLEAN
*Repaint
,
1892 OUT BOOLEAN
*NewLine
1898 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1900 UINT8
*DevicePathBuffer
;
1903 FORM_BROWSER_FORM
*RefForm
;
1906 UI_MENU_LIST
*MenuList
;
1907 BOOLEAN UpdateFormInfo
;
1909 Status
= EFI_SUCCESS
;
1910 UpdateFormInfo
= TRUE
;
1915 // Prepare the device path check, get the device path info first.
1917 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
1918 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
1919 if (StringPtr
!= NULL
) {
1920 StringLen
= StrLen (StringPtr
);
1925 // Check whether the device path string is a valid string.
1927 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0 && StringPtr
!= NULL
&& StringLen
!= 0) {
1928 if (Selection
->Form
->ModalForm
) {
1932 // Goto another Hii Package list
1934 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1935 BufferSize
= StrLen (StringPtr
) / 2;
1936 DevicePath
= AllocatePool (BufferSize
);
1937 ASSERT (DevicePath
!= NULL
);
1940 // Convert from Device Path String to DevicePath Buffer in the reverse order.
1942 DevicePathBuffer
= (UINT8
*) DevicePath
;
1943 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
1944 TemStr
[0] = StringPtr
[Index
];
1945 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
1946 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
1948 // Invalid Hex Char as the tail.
1952 if ((Index
& 1) == 0) {
1953 DevicePathBuffer
[Index
/2] = DigitUint8
;
1955 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
1958 FreePool (StringPtr
);
1960 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
1961 FreePool (DevicePath
);
1963 if (Selection
->Handle
== NULL
) {
1965 // If target Hii Handle not found, exit
1967 Selection
->Action
= UI_ACTION_EXIT
;
1968 Selection
->Statement
= NULL
;
1972 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1973 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1974 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1975 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
1976 if (Selection
->Form
->ModalForm
) {
1980 // Goto another Formset, check for uncommitted data
1982 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1984 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
1985 if (Selection
->Handle
== NULL
) {
1987 // If target Hii Handle not found, exit
1989 Selection
->Action
= UI_ACTION_EXIT
;
1990 Selection
->Statement
= NULL
;
1994 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1995 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1996 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1997 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
1999 // Check whether target From is suppressed.
2001 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
2003 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2004 if (EvaluateExpressionList(RefForm
->SuppressExpression
, TRUE
, Selection
->FormSet
, RefForm
) != ExpressFalse
) {
2006 // Form is suppressed.
2009 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2010 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2011 if (Repaint
!= NULL
) {
2019 // Goto another form inside this formset,
2021 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2023 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2024 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2025 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
2027 // Goto another Question
2029 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2031 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2032 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2034 if (Repaint
!= NULL
) {
2037 if (NewLine
!= NULL
) {
2041 UpdateFormInfo
= FALSE
;
2043 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2044 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2046 UpdateFormInfo
= FALSE
;
2049 if (UpdateFormInfo
) {
2051 // Link current form so that we can always go back when someone hits the ESC
2053 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2054 if (MenuList
== NULL
&& Selection
->CurrentMenu
!= NULL
) {
2055 MenuList
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2063 Display menu and wait for user to select one menu option, then return it.
2064 If AutoBoot is enabled, then if user doesn't select any option,
2065 after period of time, it will automatically return the first menu option.
2067 @param Selection Menu selection.
2069 @retval EFI_SUCESSS This function always return successfully for now.
2074 IN OUT UI_MENU_SELECTION
*Selection
2080 UINTN DistanceValue
;
2091 CHAR16
*OptionString
;
2092 CHAR16
*OutputString
;
2093 CHAR16
*FormattedString
;
2099 BOOLEAN InitializedFlag
;
2104 LIST_ENTRY
*TopOfScreen
;
2105 LIST_ENTRY
*SavedListEntry
;
2106 UI_MENU_OPTION
*MenuOption
;
2107 UI_MENU_OPTION
*NextMenuOption
;
2108 UI_MENU_OPTION
*SavedMenuOption
;
2109 UI_MENU_OPTION
*PreviousMenuOption
;
2110 UI_CONTROL_FLAG ControlFlag
;
2111 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2112 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2113 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2114 UI_SCREEN_OPERATION ScreenOperation
;
2115 UINT8 MinRefreshInterval
;
2117 FORM_BROWSER_STATEMENT
*Statement
;
2118 UI_MENU_LIST
*CurrentMenu
;
2119 UINTN ModalSkipColumn
;
2120 BROWSER_HOT_KEY
*HotKey
;
2122 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2124 Status
= EFI_SUCCESS
;
2125 FormattedString
= NULL
;
2126 OptionString
= NULL
;
2127 ScreenOperation
= UiNoOperation
;
2129 MinRefreshInterval
= 0;
2132 OutputString
= NULL
;
2137 MenuRefreshEntry
= gMenuRefreshHead
;
2139 NextMenuOption
= NULL
;
2140 PreviousMenuOption
= NULL
;
2141 SavedMenuOption
= NULL
;
2143 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2145 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2147 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2148 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2149 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2151 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2152 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2155 if (Selection
->Form
->ModalForm
) {
2156 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2158 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2161 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2163 Selection
->TopRow
= TopRow
;
2164 Selection
->BottomRow
= BottomRow
;
2165 Selection
->PromptCol
= Col
;
2166 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2167 Selection
->Statement
= NULL
;
2169 TopOfScreen
= gMenuOption
.ForwardLink
;
2174 // Find current Menu
2176 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2177 if (CurrentMenu
== NULL
) {
2179 // Current menu not found, add it to the menu tree
2181 CurrentMenu
= UiAddMenuList (NULL
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2183 ASSERT (CurrentMenu
!= NULL
);
2184 Selection
->CurrentMenu
= CurrentMenu
;
2186 if (Selection
->QuestionId
== 0) {
2188 // Highlight not specified, fetch it from cached menu
2190 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2191 Selection
->Sequence
= CurrentMenu
->Sequence
;
2195 // Init option as the current user's selection
2197 InitializedFlag
= TRUE
;
2198 NewPos
= gMenuOption
.ForwardLink
;
2200 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2201 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2203 ControlFlag
= CfInitialization
;
2204 Selection
->Action
= UI_ACTION_NONE
;
2206 switch (ControlFlag
) {
2207 case CfInitialization
:
2208 if (IsListEmpty (&gMenuOption
)) {
2209 ControlFlag
= CfReadKey
;
2211 ControlFlag
= CfCheckSelection
;
2215 case CfCheckSelection
:
2216 if (Selection
->Action
!= UI_ACTION_NONE
) {
2217 ControlFlag
= CfExit
;
2219 ControlFlag
= CfRepaint
;
2224 ControlFlag
= CfRefreshHighLight
;
2234 Temp
= (UINTN
) SkipValue
;
2235 Temp2
= (UINTN
) SkipValue
;
2237 if (Selection
->Form
->ModalForm
) {
2239 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2240 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2241 TopRow
- SCROLL_ARROW_HEIGHT
,
2242 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2243 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2247 LocalScreen
.LeftColumn
,
2248 LocalScreen
.RightColumn
,
2249 TopRow
- SCROLL_ARROW_HEIGHT
,
2250 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2251 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2254 UiFreeRefreshList ();
2255 MinRefreshInterval
= 0;
2257 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2258 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2259 MenuOption
->Row
= Row
;
2260 MenuOption
->Col
= Col
;
2261 if (Selection
->Form
->ModalForm
) {
2262 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2264 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2267 Statement
= MenuOption
->ThisTag
;
2268 if (Statement
->InSubtitle
) {
2269 MenuOption
->Col
+= SUBTITLE_INDENT
;
2272 if (MenuOption
->GrayOut
) {
2273 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2275 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2276 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2280 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2283 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2285 // Print Arrow for Goto button.
2288 MenuOption
->Col
- 2,
2291 GEOMETRICSHAPE_RIGHT_TRIANGLE
2295 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2296 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2297 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2300 // If there is more string to process print on the next row and increment the Skip value
2302 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2308 FreePool (OutputString
);
2317 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2318 if (EFI_ERROR (Status
)) {
2320 // Repaint to clear possible error prompt pop-up
2324 ControlFlag
= CfRepaint
;
2328 if (OptionString
!= NULL
) {
2329 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2330 ProcessStringForDateTime(MenuOption
, OptionString
, TRUE
);
2333 Width
= (UINT16
) gOptionBlockWidth
;
2336 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2337 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2338 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2341 // If there is more string to process print on the next row and increment the Skip value
2343 if (StrLen (&OptionString
[Index
]) != 0) {
2347 // Since the Number of lines for this menu entry may or may not be reflected accurately
2348 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2349 // some testing to ensure we are keeping this in-sync.
2351 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2353 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2359 FreePool (OutputString
);
2368 FreePool (OptionString
);
2372 // If Question has refresh guid, register the op-code.
2374 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2375 if (gMenuEventGuidRefreshHead
== NULL
) {
2376 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2377 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2379 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2380 while (MenuUpdateEntry
->Next
!= NULL
) {
2381 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2383 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2384 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2386 ASSERT (MenuUpdateEntry
!= NULL
);
2387 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2388 ASSERT (!EFI_ERROR (Status
));
2389 MenuUpdateEntry
->MenuOption
= MenuOption
;
2390 MenuUpdateEntry
->Selection
= Selection
;
2391 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2392 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2393 if (MenuOption
->GrayOut
) {
2394 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2396 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2401 // If Question request refresh, register the op-code
2403 if (Statement
->RefreshInterval
!= 0) {
2405 // Menu will be refreshed at minimal interval of all Questions
2406 // which have refresh request
2408 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2409 MinRefreshInterval
= Statement
->RefreshInterval
;
2412 if (gMenuRefreshHead
== NULL
) {
2413 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2414 gMenuRefreshHead
= MenuRefreshEntry
;
2416 MenuRefreshEntry
= gMenuRefreshHead
;
2417 while (MenuRefreshEntry
->Next
!= NULL
) {
2418 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2420 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2421 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2423 ASSERT (MenuRefreshEntry
!= NULL
);
2424 MenuRefreshEntry
->MenuOption
= MenuOption
;
2425 MenuRefreshEntry
->Selection
= Selection
;
2426 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2427 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2428 if (MenuOption
->GrayOut
) {
2429 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2431 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2436 // If this is a text op with secondary text information
2438 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2439 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2441 Width
= (UINT16
) gOptionBlockWidth
;
2444 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
2445 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2446 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2449 // If there is more string to process print on the next row and increment the Skip value
2451 if (StrLen (&StringPtr
[Index
]) != 0) {
2455 // Since the Number of lines for this menu entry may or may not be reflected accurately
2456 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2457 // some testing to ensure we are keeping this in-sync.
2459 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2461 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2467 FreePool (OutputString
);
2474 FreePool (StringPtr
);
2476 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2479 // Need to handle the bottom of the display
2481 if (MenuOption
->Skip
> 1) {
2482 Row
+= MenuOption
->Skip
- SkipValue
;
2485 Row
+= MenuOption
->Skip
;
2488 if (Row
> BottomRow
) {
2489 if (!ValueIsScroll (FALSE
, Link
)) {
2493 Row
= BottomRow
+ 1;
2498 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2503 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2505 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2506 TopRow
- SCROLL_ARROW_HEIGHT
,
2510 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2514 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2516 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2517 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2521 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2528 case CfRefreshHighLight
:
2530 // MenuOption: Last menu option that need to remove hilight
2531 // MenuOption is set to NULL in Repaint
2532 // NewPos: Current menu option that need to hilight
2534 ControlFlag
= CfUpdateHelpString
;
2535 if (InitializedFlag
) {
2536 InitializedFlag
= FALSE
;
2537 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2541 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2542 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2544 SavedValue
= Repaint
;
2547 if (Selection
->QuestionId
!= 0) {
2548 NewPos
= gMenuOption
.ForwardLink
;
2549 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2551 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2552 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2553 NewPos
->ForwardLink
!= &gMenuOption
) {
2554 NewPos
= NewPos
->ForwardLink
;
2555 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2557 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2559 // Target Question found, find its MenuOption
2563 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2564 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2565 Index
+= SavedMenuOption
->Skip
;
2566 if (Link
== TopOfScreen
) {
2567 Index
-= OldSkipValue
;
2569 Link
= Link
->ForwardLink
;
2571 if (NewPos
== Link
) {
2572 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2576 // Not find the selected menu in current show page.
2577 // Have two case to enter this if:
2578 // 1. Not find the menu at current page.
2579 // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
2580 // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
2582 // Base on the selected menu will show at the bottom of the page,
2583 // select the menu which will show at the top of the page.
2585 if (Link
!= NewPos
|| Index
> BottomRow
||
2586 (Link
== NewPos
&& (SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
) && (Link
!= TopOfScreen
))) {
2588 // Find the MenuOption which has the skip value for Date/Time opcode.
2590 AdjustDateAndTimePosition(FALSE
, &NewPos
);
2592 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2594 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2596 // SavedMenuOption->Row == 0 means the menu not show yet.
2598 if (SavedMenuOption
->Row
== 0) {
2599 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2603 // Base on the selected menu will show at the bottome of next page,
2604 // select the menu show at the top of the next page.
2607 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2608 Link
= Link
->BackLink
;
2609 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2610 if (SavedMenuOption
->Row
== 0) {
2611 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2613 Index
+= SavedMenuOption
->Skip
;
2617 // Found the menu which will show at the top of the page.
2619 if (Link
== NewPos
) {
2621 // The menu can show more than one pages, just show the menu at the top of the page.
2625 OldSkipValue
= SkipValue
;
2628 // Check whether need to skip some line for menu shows at the top of the page.
2630 SkipValue
= Index
- BottomRow
- 1;
2631 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2633 OldSkipValue
= SkipValue
;
2636 TopOfScreen
= Link
->ForwardLink
;
2642 ControlFlag
= CfRepaint
;
2647 // Target Question not found, highlight the default menu option
2649 NewPos
= TopOfScreen
;
2652 Selection
->QuestionId
= 0;
2655 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2656 if (MenuOption
!= NULL
) {
2658 // Remove highlight on last Menu Option
2660 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2661 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2662 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2663 if (OptionString
!= NULL
) {
2664 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2665 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2667 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2670 Width
= (UINT16
) gOptionBlockWidth
;
2671 OriginalRow
= MenuOption
->Row
;
2673 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2674 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2675 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2678 // If there is more string to process print on the next row and increment the Skip value
2680 if (StrLen (&OptionString
[Index
]) != 0) {
2684 FreePool (OutputString
);
2687 MenuOption
->Row
= OriginalRow
;
2689 FreePool (OptionString
);
2692 if (MenuOption
->GrayOut
) {
2693 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2694 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2695 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2698 OriginalRow
= MenuOption
->Row
;
2699 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2701 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2702 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2703 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2706 // If there is more string to process print on the next row and increment the Skip value
2708 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2712 FreePool (OutputString
);
2715 MenuOption
->Row
= OriginalRow
;
2716 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2722 // This is the current selected statement
2724 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2725 Statement
= MenuOption
->ThisTag
;
2726 Selection
->Statement
= Statement
;
2727 if (!IsSelectable (MenuOption
)) {
2728 Repaint
= SavedValue
;
2729 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2734 // Record highlight for current menu
2736 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2737 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2740 // Set reverse attribute
2742 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2743 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2746 // Assuming that we have a refresh linked-list created, lets annotate the
2747 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2748 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2750 if (gMenuRefreshHead
!= NULL
) {
2751 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2752 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2753 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2755 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2757 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2758 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2763 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2764 if (OptionString
!= NULL
) {
2765 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2766 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2768 Width
= (UINT16
) gOptionBlockWidth
;
2770 OriginalRow
= MenuOption
->Row
;
2772 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2773 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2774 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2777 // If there is more string to process print on the next row and increment the Skip value
2779 if (StrLen (&OptionString
[Index
]) != 0) {
2783 FreePool (OutputString
);
2786 MenuOption
->Row
= OriginalRow
;
2788 FreePool (OptionString
);
2791 OriginalRow
= MenuOption
->Row
;
2793 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2795 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2796 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2797 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2800 // If there is more string to process print on the next row and increment the Skip value
2802 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2806 FreePool (OutputString
);
2809 MenuOption
->Row
= OriginalRow
;
2814 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2817 // Clear reverse attribute
2819 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2822 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2823 // if we didn't break halfway when process CfRefreshHighLight.
2825 Repaint
= SavedValue
;
2828 case CfUpdateHelpString
:
2829 ControlFlag
= CfPrepareToReadKey
;
2830 if (Selection
->Form
->ModalForm
) {
2834 if (Repaint
|| NewLine
) {
2836 // Don't print anything if it is a NULL help token
2838 ASSERT(MenuOption
!= NULL
);
2839 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
2842 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2845 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2847 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2849 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2851 // Pad String with spaces to simulate a clearing of the previous line
2853 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2854 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2858 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2860 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2865 // Reset this flag every time we finish using it.
2871 case CfPrepareToReadKey
:
2872 ControlFlag
= CfReadKey
;
2873 ScreenOperation
= UiNoOperation
;
2877 ControlFlag
= CfScreenOperation
;
2880 // Wait for user's selection
2883 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2884 } while (Status
== EFI_TIMEOUT
);
2886 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2888 // IFR is updated in Callback of refresh opcode, re-parse it
2890 ControlFlag
= CfCheckSelection
;
2891 Selection
->Statement
= NULL
;
2895 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2897 // If we encounter error, continue to read another key in.
2899 if (EFI_ERROR (Status
)) {
2900 ControlFlag
= CfReadKey
;
2904 switch (Key
.UnicodeChar
) {
2905 case CHAR_CARRIAGE_RETURN
:
2906 if(MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
2907 ControlFlag
= CfReadKey
;
2911 ScreenOperation
= UiSelect
;
2916 // We will push the adjustment of these numeric values directly to the input handler
2917 // NOTE: we won't handle manual input numeric
2922 // If the screen has no menu items, and the user didn't select UiReset
2923 // ignore the selection and go back to reading keys.
2925 if(IsListEmpty (&gMenuOption
) || MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
2926 ControlFlag
= CfReadKey
;
2930 ASSERT(MenuOption
!= NULL
);
2931 Statement
= MenuOption
->ThisTag
;
2932 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2933 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2934 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2936 if (Key
.UnicodeChar
== '+') {
2937 gDirection
= SCAN_RIGHT
;
2939 gDirection
= SCAN_LEFT
;
2941 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2942 if (EFI_ERROR (Status
)) {
2944 // Repaint to clear possible error prompt pop-up
2949 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2951 if (OptionString
!= NULL
) {
2952 FreePool (OptionString
);
2958 ScreenOperation
= UiUp
;
2963 ScreenOperation
= UiDown
;
2967 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2969 // If the screen has no menu items, and the user didn't select UiReset
2970 // ignore the selection and go back to reading keys.
2972 if(IsListEmpty (&gMenuOption
)) {
2973 ControlFlag
= CfReadKey
;
2977 ASSERT(MenuOption
!= NULL
);
2978 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
&& !MenuOption
->ReadOnly
) {
2979 ScreenOperation
= UiSelect
;
2985 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
2986 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2987 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2992 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
2994 // ModalForm has no ESC key and Hot Key.
2996 ControlFlag
= CfReadKey
;
2997 } else if (Index
== mScanCodeNumber
) {
2999 // Check whether Key matches the registered hot key.
3002 if ((gBrowserSettingScope
== SystemLevel
) || (gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
3003 HotKey
= GetHotKeyFromRegisterList (&Key
);
3005 if (HotKey
!= NULL
) {
3006 ScreenOperation
= UiHotKey
;
3013 case CfScreenOperation
:
3014 if (ScreenOperation
!= UiReset
) {
3016 // If the screen has no menu items, and the user didn't select UiReset
3017 // ignore the selection and go back to reading keys.
3019 if (IsListEmpty (&gMenuOption
)) {
3020 ControlFlag
= CfReadKey
;
3026 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
3029 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
3030 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
3037 ControlFlag
= CfCheckSelection
;
3039 ASSERT(MenuOption
!= NULL
);
3040 Statement
= MenuOption
->ThisTag
;
3041 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
3046 // Keep highlight on current MenuOption
3048 Selection
->QuestionId
= Statement
->QuestionId
;
3050 switch (Statement
->Operand
) {
3051 case EFI_IFR_REF_OP
:
3052 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
3055 case EFI_IFR_ACTION_OP
:
3057 // Process the Config string <ConfigResp>
3059 Status
= ProcessQuestionConfig (Selection
, Statement
);
3061 if (EFI_ERROR (Status
)) {
3066 // The action button may change some Question value, so refresh the form
3068 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3071 case EFI_IFR_RESET_BUTTON_OP
:
3073 // Reset Question to default value specified by DefaultId
3075 ControlFlag
= CfUiDefault
;
3076 DefaultId
= Statement
->DefaultId
;
3081 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3083 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
3084 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3086 if (EFI_ERROR (Status
)) {
3089 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
3091 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3094 if (OptionString
!= NULL
) {
3095 FreePool (OptionString
);
3103 // We come here when someone press ESC
3105 ControlFlag
= CfCheckSelection
;
3106 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3110 ControlFlag
= CfCheckSelection
;
3111 ASSERT(MenuOption
!= NULL
);
3112 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3113 if (MenuOption
->Sequence
!= 0) {
3115 // In the middle or tail of the Date/Time op-code set, go left.
3117 ASSERT(NewPos
!= NULL
);
3118 NewPos
= NewPos
->BackLink
;
3124 ControlFlag
= CfCheckSelection
;
3125 ASSERT(MenuOption
!= NULL
);
3126 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3127 if (MenuOption
->Sequence
!= 2) {
3129 // In the middle or tail of the Date/Time op-code set, go left.
3131 ASSERT(NewPos
!= NULL
);
3132 NewPos
= NewPos
->ForwardLink
;
3138 ControlFlag
= CfCheckSelection
;
3140 SavedListEntry
= NewPos
;
3142 ASSERT(NewPos
!= NULL
);
3144 // Adjust Date/Time position before we advance forward.
3146 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3147 if (NewPos
->BackLink
!= &gMenuOption
) {
3148 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3149 ASSERT (MenuOption
!= NULL
);
3151 NewPos
= NewPos
->BackLink
;
3153 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3154 if (PreviousMenuOption
->Row
== 0) {
3155 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3157 DistanceValue
= PreviousMenuOption
->Skip
;
3159 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3160 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3162 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3164 if (Difference
< 0) {
3166 // We hit the begining MenuOption that can be focused
3167 // so we simply scroll to the top.
3169 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3170 TopOfScreen
= gMenuOption
.ForwardLink
;
3174 // Scroll up to the last page when we have arrived at top page.
3176 NewPos
= &gMenuOption
;
3177 TopOfScreen
= &gMenuOption
;
3178 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3179 ScreenOperation
= UiPageUp
;
3180 ControlFlag
= CfScreenOperation
;
3183 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3185 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3187 TopOfScreen
= NewPos
;
3191 } else if (!IsSelectable (NextMenuOption
)) {
3193 // Continue to go up until scroll to next page or the selectable option is found.
3195 ScreenOperation
= UiUp
;
3196 ControlFlag
= CfScreenOperation
;
3200 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3202 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3203 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3204 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3205 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3208 // Scroll up to the last page.
3210 NewPos
= &gMenuOption
;
3211 TopOfScreen
= &gMenuOption
;
3212 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3213 ScreenOperation
= UiPageUp
;
3214 ControlFlag
= CfScreenOperation
;
3219 ControlFlag
= CfCheckSelection
;
3221 ASSERT(NewPos
!= NULL
);
3222 if (NewPos
->BackLink
== &gMenuOption
) {
3232 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3233 Link
= Link
->BackLink
;
3234 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3235 if (PreviousMenuOption
->Row
== 0) {
3236 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3238 if (Index
< PreviousMenuOption
->Skip
) {
3242 Index
= Index
- PreviousMenuOption
->Skip
;
3245 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3246 if (TopOfScreen
== &gMenuOption
) {
3247 TopOfScreen
= gMenuOption
.ForwardLink
;
3248 NewPos
= gMenuOption
.BackLink
;
3249 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3251 } else if (TopOfScreen
!= Link
) {
3254 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3257 // Finally we know that NewPos is the last MenuOption can be focused.
3261 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3264 if (Index
+ 1 < TopRow
) {
3266 // Back up the previous option.
3268 Link
= Link
->ForwardLink
;
3272 // Move to the option in Next page.
3274 if (TopOfScreen
== &gMenuOption
) {
3275 NewPos
= gMenuOption
.BackLink
;
3276 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3279 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3283 // There are more MenuOption needing scrolling up.
3290 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3291 // Don't do this when we are already in the first page.
3293 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3294 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3298 ControlFlag
= CfCheckSelection
;
3300 ASSERT (NewPos
!= NULL
);
3301 if (NewPos
->ForwardLink
== &gMenuOption
) {
3310 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3312 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
3313 Index
= Index
+ NextMenuOption
->Skip
;
3314 Link
= Link
->ForwardLink
;
3315 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3318 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
)) {
3320 // Finally we know that NewPos is the last MenuOption can be focused.
3323 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3325 if (Index
- 1 > BottomRow
) {
3327 // Back up the previous option.
3329 Link
= Link
->BackLink
;
3332 // There are more MenuOption needing scrolling down.
3337 // Move to the option in Next page.
3339 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3343 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3344 // Don't do this when we are already in the last page.
3347 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3348 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3352 ControlFlag
= CfCheckSelection
;
3354 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3355 // to be one that progresses to the next set of op-codes, we need to advance to the last
3356 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3357 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3358 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3359 // the Date/Time op-code.
3361 SavedListEntry
= NewPos
;
3362 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3364 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3365 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3367 NewPos
= NewPos
->ForwardLink
;
3370 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3371 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3373 // We hit the end of MenuOption that can be focused
3374 // so we simply scroll to the first page.
3376 if (Difference
< 0) {
3378 // Scroll to the first page.
3380 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3381 TopOfScreen
= gMenuOption
.ForwardLink
;
3385 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3387 NewPos
= gMenuOption
.ForwardLink
;
3388 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3391 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3393 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3394 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3398 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3401 // An option might be multi-line, so we need to reflect that data in the overall skip value
3403 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3404 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3406 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3407 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3408 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3409 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3415 // If we are going to scroll, update TopOfScreen
3417 if (Temp
> BottomRow
) {
3420 // Is the current top of screen a zero-advance op-code?
3421 // If so, keep moving forward till we hit a >0 advance op-code
3423 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3426 // If bottom op-code is more than one line or top op-code is more than one line
3428 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3430 // Is the bottom op-code greater than or equal in size to the top op-code?
3432 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3434 // Skip the top op-code
3436 TopOfScreen
= TopOfScreen
->ForwardLink
;
3437 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3439 OldSkipValue
= Difference
;
3441 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3444 // If we have a remainder, skip that many more op-codes until we drain the remainder
3446 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3448 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3450 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3451 TopOfScreen
= TopOfScreen
->ForwardLink
;
3452 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3455 // Since we will act on this op-code in the next routine, and increment the
3456 // SkipValue, set the skips to one less than what is required.
3458 SkipValue
= Difference
- 1;
3462 // Since we will act on this op-code in the next routine, and increment the
3463 // SkipValue, set the skips to one less than what is required.
3465 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3468 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3469 TopOfScreen
= TopOfScreen
->ForwardLink
;
3472 SkipValue
= OldSkipValue
;
3476 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3477 // Let's set a skip flag to smoothly scroll the top of the screen.
3479 if (SavedMenuOption
->Skip
> 1) {
3480 if (SavedMenuOption
== NextMenuOption
) {
3485 } else if (SavedMenuOption
->Skip
== 1) {
3489 TopOfScreen
= TopOfScreen
->ForwardLink
;
3491 } while (SavedMenuOption
->Skip
== 0);
3494 OldSkipValue
= SkipValue
;
3495 } else if (!IsSelectable (NextMenuOption
)) {
3497 // Continue to go down until scroll to next page or the selectable option is found.
3499 ScreenOperation
= UiDown
;
3500 ControlFlag
= CfScreenOperation
;
3503 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3505 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3509 // Scroll to the first page.
3511 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3512 TopOfScreen
= gMenuOption
.ForwardLink
;
3516 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3519 NewPos
= gMenuOption
.ForwardLink
;
3520 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3524 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3526 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3527 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3531 ControlFlag
= CfCheckSelection
;
3533 Status
= EFI_SUCCESS
;
3535 // Discard changes. After it, no NV flag is showed.
3537 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3538 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3539 if (!EFI_ERROR (Status
)) {
3540 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3541 Selection
->Statement
= NULL
;
3542 gResetRequired
= FALSE
;
3545 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3546 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3548 // Still show current page.
3550 Selection
->Action
= UI_ACTION_NONE
;
3558 // Reterieve default setting. After it. NV flag will be showed.
3560 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3561 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
);
3562 if (!EFI_ERROR (Status
)) {
3563 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3564 Selection
->Statement
= NULL
;
3565 gResetRequired
= TRUE
;
3568 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3569 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3571 // Still show current page.
3573 Selection
->Action
= UI_ACTION_NONE
;
3581 // Save changes. After it, no NV flag is showed.
3583 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3584 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3585 if (!EFI_ERROR (Status
)) {
3586 ASSERT(MenuOption
!= NULL
);
3587 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3588 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3591 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3592 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3594 // Still show current page.
3596 Selection
->Action
= UI_ACTION_NONE
;
3604 // Set Reset required Flag
3606 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3607 gResetRequired
= TRUE
;
3613 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3615 // Form Exit without saving, Similar to ESC Key.
3616 // FormSet Exit without saving, Exit SendForm.
3617 // System Exit without saving, CallExitHandler and Exit SendForm.
3619 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3620 if (gBrowserSettingScope
== FormLevel
) {
3621 ControlFlag
= CfUiReset
;
3622 } else if (gBrowserSettingScope
== FormSetLevel
) {
3623 Selection
->Action
= UI_ACTION_EXIT
;
3624 } else if (gBrowserSettingScope
== SystemLevel
) {
3625 if (ExitHandlerFunction
!= NULL
) {
3626 ExitHandlerFunction ();
3628 Selection
->Action
= UI_ACTION_EXIT
;
3630 Selection
->Statement
= NULL
;
3635 ControlFlag
= CfCheckSelection
;
3637 // Reset to default value for all forms in the whole system.
3639 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
);
3641 if (!EFI_ERROR (Status
)) {
3642 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3643 Selection
->Statement
= NULL
;
3644 gResetRequired
= TRUE
;
3648 case CfUiNoOperation
:
3649 ControlFlag
= CfCheckSelection
;
3653 UiFreeRefreshList ();
3655 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3656 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3657 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3658 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");