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
;
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 (Statement
->GrayOutExpression
!= NULL
) {
735 MenuOption
->GrayOut
= Statement
->GrayOutExpression
->Result
.Value
.b
;
739 // If the form or the question has the lock attribute, deal same as grayout.
741 if (Form
->Locked
|| Statement
->Locked
) {
742 MenuOption
->GrayOut
= TRUE
;
745 switch (Statement
->Operand
) {
746 case EFI_IFR_ORDERED_LIST_OP
:
747 case EFI_IFR_ONE_OF_OP
:
748 case EFI_IFR_NUMERIC_OP
:
749 case EFI_IFR_TIME_OP
:
750 case EFI_IFR_DATE_OP
:
751 case EFI_IFR_CHECKBOX_OP
:
752 case EFI_IFR_PASSWORD_OP
:
753 case EFI_IFR_STRING_OP
:
755 // User could change the value of these items
757 MenuOption
->IsQuestion
= TRUE
;
760 case EFI_IFR_TEXT_OP
:
761 if (FeaturePcdGet (PcdBrowserGrayOutTextStatement
)) {
763 // Initializing GrayOut option as TRUE for Text setup options
764 // so that those options will be Gray in colour and un selectable.
766 MenuOption
->GrayOut
= TRUE
;
770 MenuOption
->IsQuestion
= FALSE
;
774 if ((Statement
->ValueExpression
!= NULL
) ||
775 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
776 MenuOption
->ReadOnly
= TRUE
;
779 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
787 Routine used to abstract a generic dialog interface and return the selected key or string
789 @param NumberOfLines The number of lines for the dialog box
790 @param HotKey Defines whether a single character is parsed
791 (TRUE) and returned in KeyValue or a string is
792 returned in StringBuffer. Two special characters
793 are considered when entering a string, a SCAN_ESC
794 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
795 string input and returns
796 @param MaximumStringSize The maximum size in bytes of a typed in string
797 (each character is a CHAR16) and the minimum
798 string returned is two bytes
799 @param StringBuffer The passed in pointer to the buffer which will
800 hold the typed in string if HotKey is FALSE
801 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
802 @param ... A series of (quantity == NumberOfLines) text
803 strings which will be used to construct the dialog
806 @retval EFI_SUCCESS Displayed dialog and received user interaction
807 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
808 (StringBuffer == NULL) && (HotKey == FALSE))
809 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
815 IN UINTN NumberOfLines
,
817 IN UINTN MaximumStringSize
,
818 OUT CHAR16
*StringBuffer
,
819 OUT EFI_INPUT_KEY
*KeyValue
,
828 CHAR16
*BufferedString
;
835 BOOLEAN SelectionComplete
;
837 UINTN CurrentAttribute
;
838 UINTN DimensionsWidth
;
839 UINTN DimensionsHeight
;
841 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
842 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
844 SelectionComplete
= FALSE
;
846 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
847 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
848 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
851 ASSERT (BufferedString
);
853 VA_START (Marker
, KeyValue
);
856 // Zero the outgoing buffer
858 ZeroMem (StringBuffer
, MaximumStringSize
);
861 if (KeyValue
== NULL
) {
862 return EFI_INVALID_PARAMETER
;
865 if (StringBuffer
== NULL
) {
866 return EFI_INVALID_PARAMETER
;
872 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
877 // Determine the largest string in the dialog box
878 // Notice we are starting with 1 since String is the first string
880 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
881 StackString
= VA_ARG (Marker
, CHAR16
*);
883 if (StackString
[0] == L
' ') {
884 InputOffset
= Count
+ 1;
887 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
889 // Size of the string visually and subtract the width by one for the null-terminator
891 LargestString
= (GetStringWidth (StackString
) / 2);
896 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
897 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
904 VA_START (Marker
, KeyValue
);
905 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
909 // Take the first key typed and report it back?
912 Status
= WaitForKeyStroke (&Key
);
913 ASSERT_EFI_ERROR (Status
);
914 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
918 Status
= WaitForKeyStroke (&Key
);
920 switch (Key
.UnicodeChar
) {
922 switch (Key
.ScanCode
) {
924 FreePool (TempString
);
925 FreePool (BufferedString
);
926 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
927 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
928 return EFI_DEVICE_ERROR
;
936 case CHAR_CARRIAGE_RETURN
:
937 SelectionComplete
= TRUE
;
938 FreePool (TempString
);
939 FreePool (BufferedString
);
940 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
941 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
946 if (StringBuffer
[0] != CHAR_NULL
) {
947 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
948 TempString
[Index
] = StringBuffer
[Index
];
951 // Effectively truncate string by 1 character
953 TempString
[Index
- 1] = CHAR_NULL
;
954 StrCpy (StringBuffer
, TempString
);
959 // If it is the beginning of the string, don't worry about checking maximum limits
961 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
962 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
963 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
964 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
965 KeyPad
[0] = Key
.UnicodeChar
;
966 KeyPad
[1] = CHAR_NULL
;
967 StrCat (StringBuffer
, KeyPad
);
968 StrCat (TempString
, KeyPad
);
971 // If the width of the input string is now larger than the screen, we nee to
972 // adjust the index to start printing portions of the string
974 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
976 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
978 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
979 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
984 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
985 BufferedString
[Count
] = StringBuffer
[Index
];
988 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
991 } while (!SelectionComplete
);
994 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
995 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1000 Draw a pop up windows based on the dimension, number of lines and
1003 @param RequestedWidth The width of the pop-up.
1004 @param NumberOfLines The number of lines.
1005 @param Marker The variable argument list for the list of string to be printed.
1010 IN UINTN RequestedWidth
,
1011 IN UINTN NumberOfLines
,
1023 UINTN DimensionsWidth
;
1024 UINTN DimensionsHeight
;
1026 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
1027 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
1029 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1031 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
1032 RequestedWidth
= DimensionsWidth
- 2;
1036 // Subtract the PopUp width from total Columns, allow for one space extra on
1037 // each end plus a border.
1039 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
1040 End
= Start
+ RequestedWidth
+ 1;
1042 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
1043 Bottom
= Top
+ NumberOfLines
+ 2;
1045 Character
= BOXDRAW_DOWN_RIGHT
;
1046 PrintCharAt (Start
, Top
, Character
);
1047 Character
= BOXDRAW_HORIZONTAL
;
1048 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1049 PrintChar (Character
);
1052 Character
= BOXDRAW_DOWN_LEFT
;
1053 PrintChar (Character
);
1054 Character
= BOXDRAW_VERTICAL
;
1057 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
1058 String
= VA_ARG (Marker
, CHAR16
*);
1061 // This will clear the background of the line - we never know who might have been
1062 // here before us. This differs from the next clear in that it used the non-reverse
1063 // video for normal printing.
1065 if (GetStringWidth (String
) / 2 > 1) {
1066 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1070 // Passing in a space results in the assumption that this is where typing will occur
1072 if (String
[0] == L
' ') {
1073 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
1077 // Passing in a NULL results in a blank space
1079 if (String
[0] == CHAR_NULL
) {
1080 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1084 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
1088 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1089 PrintCharAt (Start
, Index
+ 1, Character
);
1090 PrintCharAt (End
- 1, Index
+ 1, Character
);
1093 Character
= BOXDRAW_UP_RIGHT
;
1094 PrintCharAt (Start
, Bottom
- 1, Character
);
1095 Character
= BOXDRAW_HORIZONTAL
;
1096 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1097 PrintChar (Character
);
1100 Character
= BOXDRAW_UP_LEFT
;
1101 PrintChar (Character
);
1105 Draw a pop up windows based on the dimension, number of lines and
1108 @param RequestedWidth The width of the pop-up.
1109 @param NumberOfLines The number of lines.
1110 @param ... A series of text strings that displayed in the pop-up.
1115 CreateMultiStringPopUp (
1116 IN UINTN RequestedWidth
,
1117 IN UINTN NumberOfLines
,
1123 VA_START (Marker
, NumberOfLines
);
1125 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1132 Update status bar on the bottom of menu.
1134 @param Selection Current Selction info.
1135 @param MessageType The type of message to be shown.
1136 @param Flags The flags in Question header.
1137 @param State Set or clear.
1142 IN UI_MENU_SELECTION
*Selection
,
1143 IN UINTN MessageType
,
1149 CHAR16
*NvUpdateMessage
;
1150 CHAR16
*InputErrorMessage
;
1152 FORM_BROWSER_FORMSET
*LocalFormSet
;
1153 FORM_BROWSER_STATEMENT
*Question
;
1155 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1156 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1158 switch (MessageType
) {
1161 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1163 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1164 gScreenDimensions
.BottomRow
- 1,
1169 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1170 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1171 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1174 mInputError
= FALSE
;
1178 case NV_UPDATE_REQUIRED
:
1180 // Global setting support. Show configuration change on every form.
1183 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1185 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1186 Question
= Selection
->Statement
;
1187 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1189 // Update only for Question value that need to be saved into Storage.
1191 Selection
->Form
->NvUpdateRequired
= TRUE
;
1195 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1196 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1198 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1199 gScreenDimensions
.BottomRow
- 1,
1204 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1205 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1207 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1208 gScreenDimensions
.BottomRow
- 1,
1215 case REFRESH_STATUS_BAR
:
1217 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1220 switch (gBrowserSettingScope
) {
1223 // Check the maintain list to see whether there is any change.
1225 Link
= GetFirstNode (&gBrowserFormSetList
);
1226 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1227 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1228 if (IsNvUpdateRequired(LocalFormSet
)) {
1229 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1232 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1237 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1248 FreePool (InputErrorMessage
);
1249 FreePool (NvUpdateMessage
);
1255 Get the supported width for a particular op-code
1257 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1258 @param Handle The handle in the HII database being used
1260 @return Returns the number of CHAR16 characters that is support.
1265 IN FORM_BROWSER_STATEMENT
*Statement
,
1266 IN EFI_HII_HANDLE Handle
1276 // See if the second text parameter is really NULL
1278 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1279 String
= GetToken (Statement
->TextTwo
, Handle
);
1280 Size
= StrLen (String
);
1284 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1285 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1286 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1287 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1288 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1290 // Allow a wide display if text op-code and no secondary text op-code
1292 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1294 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1296 Width
= (UINT16
) gPromptBlockWidth
;
1299 if (Statement
->InSubtitle
) {
1300 Width
-= SUBTITLE_INDENT
;
1303 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1307 Will copy LineWidth amount of a string in the OutputString buffer and return the
1308 number of CHAR16 characters that were copied into the OutputString buffer.
1310 @param InputString String description for this option.
1311 @param LineWidth Width of the desired string to extract in CHAR16
1313 @param Index Where in InputString to start the copy process
1314 @param OutputString Buffer to copy the string into
1316 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1321 IN CHAR16
*InputString
,
1322 IN UINT16 LineWidth
,
1323 IN OUT UINTN
*Index
,
1324 OUT CHAR16
**OutputString
1330 if (GetLineByWidthFinished
) {
1331 GetLineByWidthFinished
= FALSE
;
1338 *OutputString
= AllocateZeroPool (((UINTN
) (LineWidth
+ 1) * 2));
1341 // Ensure we have got a valid buffer
1343 if (*OutputString
!= NULL
) {
1346 //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.
1347 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1349 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1350 *Index
= *Index
+ 2;
1354 // Fast-forward the string and see if there is a carriage-return in the string
1356 for (; (InputString
[*Index
+ Count2
] != CHAR_CARRIAGE_RETURN
) && (Count2
!= LineWidth
); Count2
++)
1360 // Copy the desired LineWidth of data to the output buffer.
1361 // Also make sure that we don't copy more than the string.
1362 // Also make sure that if there are linefeeds, we account for them.
1364 if ((StrSize (&InputString
[*Index
]) <= ((UINTN
) (LineWidth
+ 1) * 2)) &&
1365 (StrSize (&InputString
[*Index
]) <= ((UINTN
) (Count2
+ 1) * 2))
1368 // Convert to CHAR16 value and show that we are done with this operation
1370 LineWidth
= (UINT16
) ((StrSize (&InputString
[*Index
]) - 2) / 2);
1371 if (LineWidth
!= 0) {
1372 GetLineByWidthFinished
= TRUE
;
1375 if (Count2
== LineWidth
) {
1377 // Rewind the string from the maximum size until we see a space to break the line
1379 for (; (InputString
[*Index
+ LineWidth
] != CHAR_SPACE
) && (LineWidth
!= 0); LineWidth
--)
1381 if (LineWidth
== 0) {
1389 CopyMem (*OutputString
, &InputString
[*Index
], LineWidth
* 2);
1392 // If currently pointing to a space, increment the index to the first non-space character
1395 (InputString
[*Index
+ LineWidth
] == CHAR_SPACE
) || (InputString
[*Index
+ LineWidth
] == CHAR_CARRIAGE_RETURN
);
1399 *Index
= (UINT16
) (*Index
+ LineWidth
);
1408 Update display lines for a Menu Option.
1410 @param Selection The user's selection.
1411 @param MenuOption The MenuOption to be checked.
1415 UpdateOptionSkipLines (
1416 IN UI_MENU_SELECTION
*Selection
,
1417 IN UI_MENU_OPTION
*MenuOption
1424 CHAR16
*OutputString
;
1425 CHAR16
*OptionString
;
1428 OptionString
= NULL
;
1429 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1431 if (OptionString
!= NULL
) {
1432 Width
= (UINT16
) gOptionBlockWidth
;
1436 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
1438 // If there is more string to process print on the next row and increment the Skip value
1440 if (StrLen (&OptionString
[Index
]) != 0) {
1443 // Since the Number of lines for this menu entry may or may not be reflected accurately
1444 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1445 // some testing to ensure we are keeping this in-sync.
1447 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1449 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1454 FreePool (OutputString
);
1460 if (OptionString
!= NULL
) {
1461 FreePool (OptionString
);
1467 Check whether this Menu Option could be highlighted.
1469 This is an internal function.
1471 @param MenuOption The MenuOption to be checked.
1473 @retval TRUE This Menu Option is selectable.
1474 @retval FALSE This Menu Option could not be selected.
1479 UI_MENU_OPTION
*MenuOption
1482 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1483 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1492 Determine if the menu is the last menu that can be selected.
1494 This is an internal function.
1496 @param Direction The scroll direction. False is down. True is up.
1497 @param CurrentPos The current focus.
1499 @return FALSE -- the menu isn't the last menu that can be selected.
1500 @return TRUE -- the menu is the last menu that can be selected.
1505 IN BOOLEAN Direction
,
1506 IN LIST_ENTRY
*CurrentPos
1511 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1513 if (Temp
== &gMenuOption
) {
1522 Move to next selectable statement.
1524 This is an internal function.
1526 @param Selection Menu selection.
1527 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1528 @param CurrentPosition Current position.
1529 @param GapToTop Gap position to top or bottom.
1531 @return The row distance from current MenuOption to next selectable MenuOption.
1535 MoveToNextStatement (
1536 IN UI_MENU_SELECTION
*Selection
,
1538 IN OUT LIST_ENTRY
**CurrentPosition
,
1544 UI_MENU_OPTION
*NextMenuOption
;
1545 UI_MENU_OPTION
*PreMenuOption
;
1548 Pos
= *CurrentPosition
;
1549 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1552 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1553 if (NextMenuOption
->Row
== 0) {
1554 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1557 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1559 // Current Position doesn't need to be caculated when go up.
1560 // Caculate distanct at first when go up
1562 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1563 NextMenuOption
= PreMenuOption
;
1566 Distance
+= NextMenuOption
->Skip
;
1568 if (IsSelectable (NextMenuOption
)) {
1571 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1580 // Caculate distanct at later when go down
1582 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1583 NextMenuOption
= PreMenuOption
;
1586 Distance
+= NextMenuOption
->Skip
;
1588 PreMenuOption
= NextMenuOption
;
1589 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1592 *CurrentPosition
= &NextMenuOption
->Link
;
1598 Adjust Data and Time position accordingly.
1599 Data format : [01/02/2004] [11:22:33]
1600 Line number : 0 0 1 0 0 1
1602 This is an internal function.
1604 @param DirectionUp the up or down direction. False is down. True is
1606 @param CurrentPosition Current position. On return: Point to the last
1607 Option (Year or Second) if up; Point to the first
1608 Option (Month or Hour) if down.
1610 @return Return line number to pad. It is possible that we stand on a zero-advance
1611 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1615 AdjustDateAndTimePosition (
1616 IN BOOLEAN DirectionUp
,
1617 IN OUT LIST_ENTRY
**CurrentPosition
1621 LIST_ENTRY
*NewPosition
;
1622 UI_MENU_OPTION
*MenuOption
;
1623 UINTN PadLineNumber
;
1626 NewPosition
= *CurrentPosition
;
1627 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1629 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1630 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1632 // Calculate the distance from current position to the last Date/Time MenuOption
1635 while (MenuOption
->Skip
== 0) {
1637 NewPosition
= NewPosition
->ForwardLink
;
1638 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1642 NewPosition
= *CurrentPosition
;
1645 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1646 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1647 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1648 // checking can be done.
1650 while (Count
++ < 2) {
1651 NewPosition
= NewPosition
->BackLink
;
1655 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1656 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1657 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1658 // checking can be done.
1660 while (Count
-- > 0) {
1661 NewPosition
= NewPosition
->ForwardLink
;
1665 *CurrentPosition
= NewPosition
;
1668 return PadLineNumber
;
1672 Find HII Handle in the HII database associated with given Device Path.
1674 If DevicePath is NULL, then ASSERT.
1676 @param DevicePath Device Path associated with the HII package list
1679 @retval Handle HII package list Handle associated with the Device
1681 @retval NULL Hii Package list handle is not found.
1686 DevicePathToHiiHandle (
1687 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1691 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1696 EFI_HANDLE DriverHandle
;
1697 EFI_HII_HANDLE
*HiiHandles
;
1698 EFI_HII_HANDLE HiiHandle
;
1700 ASSERT (DevicePath
!= NULL
);
1702 TmpDevicePath
= DevicePath
;
1704 // Locate Device Path Protocol handle buffer
1706 Status
= gBS
->LocateDevicePath (
1707 &gEfiDevicePathProtocolGuid
,
1711 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1716 // Retrieve all HII Handles from HII database
1718 BufferSize
= 0x1000;
1719 HiiHandles
= AllocatePool (BufferSize
);
1720 ASSERT (HiiHandles
!= NULL
);
1721 Status
= mHiiDatabase
->ListPackageLists (
1723 EFI_HII_PACKAGE_TYPE_ALL
,
1728 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1729 FreePool (HiiHandles
);
1730 HiiHandles
= AllocatePool (BufferSize
);
1731 ASSERT (HiiHandles
!= NULL
);
1733 Status
= mHiiDatabase
->ListPackageLists (
1735 EFI_HII_PACKAGE_TYPE_ALL
,
1742 if (EFI_ERROR (Status
)) {
1743 FreePool (HiiHandles
);
1748 // Search Hii Handle by Driver Handle
1751 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1752 for (Index
= 0; Index
< HandleCount
; Index
++) {
1753 Status
= mHiiDatabase
->GetPackageListHandle (
1758 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1759 HiiHandle
= HiiHandles
[Index
];
1764 FreePool (HiiHandles
);
1769 Find HII Handle in the HII database associated with given form set guid.
1771 If FormSetGuid is NULL, then ASSERT.
1773 @param ComparingGuid FormSet Guid associated with the HII package list
1776 @retval Handle HII package list Handle associated with the Device
1778 @retval NULL Hii Package list handle is not found.
1782 FormSetGuidToHiiHandle (
1783 EFI_GUID
*ComparingGuid
1786 EFI_HII_HANDLE
*HiiHandles
;
1788 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1792 UINT32 PackageListLength
;
1793 EFI_HII_PACKAGE_HEADER PackageHeader
;
1797 EFI_HII_HANDLE HiiHandle
;
1799 ASSERT (ComparingGuid
!= NULL
);
1803 // Get all the Hii handles
1805 HiiHandles
= HiiGetHiiHandles (NULL
);
1806 ASSERT (HiiHandles
!= NULL
);
1809 // Search for formset of each class type
1811 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1813 HiiPackageList
= NULL
;
1814 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1815 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1816 HiiPackageList
= AllocatePool (BufferSize
);
1817 ASSERT (HiiPackageList
!= NULL
);
1819 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1821 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1826 // Get Form package from this HII package List
1828 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1830 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1832 while (Offset
< PackageListLength
) {
1833 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1834 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1836 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1838 // Search FormSet in this Form Package
1840 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1841 while (Offset2
< PackageHeader
.Length
) {
1842 OpCodeData
= Package
+ Offset2
;
1844 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1846 // Try to compare against formset GUID
1848 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1849 HiiHandle
= HiiHandles
[Index
];
1854 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1857 if (HiiHandle
!= NULL
) {
1860 Offset
+= PackageHeader
.Length
;
1863 FreePool (HiiPackageList
);
1864 if (HiiHandle
!= NULL
) {
1869 FreePool (HiiHandles
);
1875 Process the goto op code, update the info in the selection structure.
1877 @param Statement The statement belong to goto op code.
1878 @param Selection The selection info.
1879 @param Repaint Whether need to repaint the menu.
1880 @param NewLine Whether need to create new line.
1882 @retval EFI_SUCCESS The menu process successfully.
1883 @return Other value if the process failed.
1887 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
1888 IN OUT UI_MENU_SELECTION
*Selection
,
1889 OUT BOOLEAN
*Repaint
,
1890 OUT BOOLEAN
*NewLine
1896 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1898 UINT8
*DevicePathBuffer
;
1901 FORM_BROWSER_FORM
*RefForm
;
1904 UI_MENU_LIST
*MenuList
;
1905 BOOLEAN UpdateFormInfo
;
1907 Status
= EFI_SUCCESS
;
1908 UpdateFormInfo
= TRUE
;
1913 // Prepare the device path check, get the device path info first.
1915 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
1916 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
1917 if (StringPtr
!= NULL
) {
1918 StringLen
= StrLen (StringPtr
);
1923 // Check whether the device path string is a valid string.
1925 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0 && StringPtr
!= NULL
&& StringLen
!= 0) {
1926 if (Selection
->Form
->ModalForm
) {
1930 // Goto another Hii Package list
1932 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1933 BufferSize
= StrLen (StringPtr
) / 2;
1934 DevicePath
= AllocatePool (BufferSize
);
1935 ASSERT (DevicePath
!= NULL
);
1938 // Convert from Device Path String to DevicePath Buffer in the reverse order.
1940 DevicePathBuffer
= (UINT8
*) DevicePath
;
1941 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
1942 TemStr
[0] = StringPtr
[Index
];
1943 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
1944 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
1946 // Invalid Hex Char as the tail.
1950 if ((Index
& 1) == 0) {
1951 DevicePathBuffer
[Index
/2] = DigitUint8
;
1953 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
1956 FreePool (StringPtr
);
1958 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
1959 FreePool (DevicePath
);
1961 if (Selection
->Handle
== NULL
) {
1963 // If target Hii Handle not found, exit
1965 Selection
->Action
= UI_ACTION_EXIT
;
1966 Selection
->Statement
= NULL
;
1970 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1971 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1972 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1973 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
1974 if (Selection
->Form
->ModalForm
) {
1978 // Goto another Formset, check for uncommitted data
1980 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
1982 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
1983 if (Selection
->Handle
== NULL
) {
1985 // If target Hii Handle not found, exit
1987 Selection
->Action
= UI_ACTION_EXIT
;
1988 Selection
->Statement
= NULL
;
1992 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
1993 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
1994 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
1995 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
1997 // Check whether target From is suppressed.
1999 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
2001 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2002 Status
= EvaluateExpression (Selection
->FormSet
, RefForm
, RefForm
->SuppressExpression
);
2003 if (EFI_ERROR (Status
)) {
2007 if (RefForm
->SuppressExpression
->Result
.Value
.b
) {
2009 // Form is suppressed.
2012 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2013 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2014 if (Repaint
!= NULL
) {
2022 // Goto another form inside this formset,
2024 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2026 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2027 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2028 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
2030 // Goto another Question
2032 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2034 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2035 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2037 if (Repaint
!= NULL
) {
2040 if (NewLine
!= NULL
) {
2044 UpdateFormInfo
= FALSE
;
2046 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2047 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2049 UpdateFormInfo
= FALSE
;
2052 if (UpdateFormInfo
) {
2054 // Link current form so that we can always go back when someone hits the ESC
2056 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2057 if (MenuList
== NULL
&& Selection
->CurrentMenu
!= NULL
) {
2058 MenuList
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2066 Display menu and wait for user to select one menu option, then return it.
2067 If AutoBoot is enabled, then if user doesn't select any option,
2068 after period of time, it will automatically return the first menu option.
2070 @param Selection Menu selection.
2072 @retval EFI_SUCESSS This function always return successfully for now.
2077 IN OUT UI_MENU_SELECTION
*Selection
2083 UINTN DistanceValue
;
2094 CHAR16
*OptionString
;
2095 CHAR16
*OutputString
;
2096 CHAR16
*FormattedString
;
2102 BOOLEAN InitializedFlag
;
2107 LIST_ENTRY
*TopOfScreen
;
2108 LIST_ENTRY
*SavedListEntry
;
2109 UI_MENU_OPTION
*MenuOption
;
2110 UI_MENU_OPTION
*NextMenuOption
;
2111 UI_MENU_OPTION
*SavedMenuOption
;
2112 UI_MENU_OPTION
*PreviousMenuOption
;
2113 UI_CONTROL_FLAG ControlFlag
;
2114 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2115 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2116 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2117 UI_SCREEN_OPERATION ScreenOperation
;
2118 UINT8 MinRefreshInterval
;
2120 FORM_BROWSER_STATEMENT
*Statement
;
2121 UI_MENU_LIST
*CurrentMenu
;
2122 UINTN ModalSkipColumn
;
2123 BROWSER_HOT_KEY
*HotKey
;
2125 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2127 Status
= EFI_SUCCESS
;
2128 FormattedString
= NULL
;
2129 OptionString
= NULL
;
2130 ScreenOperation
= UiNoOperation
;
2132 MinRefreshInterval
= 0;
2135 OutputString
= NULL
;
2140 MenuRefreshEntry
= gMenuRefreshHead
;
2142 NextMenuOption
= NULL
;
2143 PreviousMenuOption
= NULL
;
2144 SavedMenuOption
= NULL
;
2146 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2148 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2150 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2151 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2152 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2154 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2155 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2158 if (Selection
->Form
->ModalForm
) {
2159 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2161 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2164 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2166 Selection
->TopRow
= TopRow
;
2167 Selection
->BottomRow
= BottomRow
;
2168 Selection
->PromptCol
= Col
;
2169 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2170 Selection
->Statement
= NULL
;
2172 TopOfScreen
= gMenuOption
.ForwardLink
;
2177 // Find current Menu
2179 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2180 if (CurrentMenu
== NULL
) {
2182 // Current menu not found, add it to the menu tree
2184 CurrentMenu
= UiAddMenuList (NULL
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2186 ASSERT (CurrentMenu
!= NULL
);
2187 Selection
->CurrentMenu
= CurrentMenu
;
2189 if (Selection
->QuestionId
== 0) {
2191 // Highlight not specified, fetch it from cached menu
2193 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2194 Selection
->Sequence
= CurrentMenu
->Sequence
;
2198 // Init option as the current user's selection
2200 InitializedFlag
= TRUE
;
2201 NewPos
= gMenuOption
.ForwardLink
;
2203 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2204 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2206 ControlFlag
= CfInitialization
;
2207 Selection
->Action
= UI_ACTION_NONE
;
2209 switch (ControlFlag
) {
2210 case CfInitialization
:
2211 if (IsListEmpty (&gMenuOption
)) {
2212 ControlFlag
= CfReadKey
;
2214 ControlFlag
= CfCheckSelection
;
2218 case CfCheckSelection
:
2219 if (Selection
->Action
!= UI_ACTION_NONE
) {
2220 ControlFlag
= CfExit
;
2222 ControlFlag
= CfRepaint
;
2227 ControlFlag
= CfRefreshHighLight
;
2237 Temp
= (UINTN
) SkipValue
;
2238 Temp2
= (UINTN
) SkipValue
;
2240 if (Selection
->Form
->ModalForm
) {
2242 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2243 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2244 TopRow
- SCROLL_ARROW_HEIGHT
,
2245 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2246 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2250 LocalScreen
.LeftColumn
,
2251 LocalScreen
.RightColumn
,
2252 TopRow
- SCROLL_ARROW_HEIGHT
,
2253 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2254 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2257 UiFreeRefreshList ();
2258 MinRefreshInterval
= 0;
2260 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2261 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2262 MenuOption
->Row
= Row
;
2263 MenuOption
->Col
= Col
;
2264 if (Selection
->Form
->ModalForm
) {
2265 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2267 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2270 Statement
= MenuOption
->ThisTag
;
2271 if (Statement
->InSubtitle
) {
2272 MenuOption
->Col
+= SUBTITLE_INDENT
;
2275 if (MenuOption
->GrayOut
) {
2276 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2278 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2279 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2283 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2286 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2288 // Print Arrow for Goto button.
2291 MenuOption
->Col
- 2,
2294 GEOMETRICSHAPE_RIGHT_TRIANGLE
2298 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2299 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2300 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2303 // If there is more string to process print on the next row and increment the Skip value
2305 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2311 FreePool (OutputString
);
2320 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2321 if (EFI_ERROR (Status
)) {
2323 // Repaint to clear possible error prompt pop-up
2327 ControlFlag
= CfRepaint
;
2331 if (OptionString
!= NULL
) {
2332 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2333 ProcessStringForDateTime(MenuOption
, OptionString
, TRUE
);
2336 Width
= (UINT16
) gOptionBlockWidth
;
2339 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2340 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2341 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2344 // If there is more string to process print on the next row and increment the Skip value
2346 if (StrLen (&OptionString
[Index
]) != 0) {
2350 // Since the Number of lines for this menu entry may or may not be reflected accurately
2351 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2352 // some testing to ensure we are keeping this in-sync.
2354 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2356 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2362 FreePool (OutputString
);
2371 FreePool (OptionString
);
2375 // If Question has refresh guid, register the op-code.
2377 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2378 if (gMenuEventGuidRefreshHead
== NULL
) {
2379 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2380 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2382 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2383 while (MenuUpdateEntry
->Next
!= NULL
) {
2384 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2386 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2387 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2389 ASSERT (MenuUpdateEntry
!= NULL
);
2390 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2391 ASSERT (!EFI_ERROR (Status
));
2392 MenuUpdateEntry
->MenuOption
= MenuOption
;
2393 MenuUpdateEntry
->Selection
= Selection
;
2394 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2395 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2396 if (MenuOption
->GrayOut
) {
2397 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2399 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2404 // If Question request refresh, register the op-code
2406 if (Statement
->RefreshInterval
!= 0) {
2408 // Menu will be refreshed at minimal interval of all Questions
2409 // which have refresh request
2411 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2412 MinRefreshInterval
= Statement
->RefreshInterval
;
2415 if (gMenuRefreshHead
== NULL
) {
2416 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2417 gMenuRefreshHead
= MenuRefreshEntry
;
2419 MenuRefreshEntry
= gMenuRefreshHead
;
2420 while (MenuRefreshEntry
->Next
!= NULL
) {
2421 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2423 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2424 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2426 ASSERT (MenuRefreshEntry
!= NULL
);
2427 MenuRefreshEntry
->MenuOption
= MenuOption
;
2428 MenuRefreshEntry
->Selection
= Selection
;
2429 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2430 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2431 if (MenuOption
->GrayOut
) {
2432 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2434 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2439 // If this is a text op with secondary text information
2441 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2442 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2444 Width
= (UINT16
) gOptionBlockWidth
;
2447 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &Index
, &OutputString
) != 0x0000;) {
2448 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2449 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2452 // If there is more string to process print on the next row and increment the Skip value
2454 if (StrLen (&StringPtr
[Index
]) != 0) {
2458 // Since the Number of lines for this menu entry may or may not be reflected accurately
2459 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2460 // some testing to ensure we are keeping this in-sync.
2462 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2464 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2470 FreePool (OutputString
);
2477 FreePool (StringPtr
);
2479 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2482 // Need to handle the bottom of the display
2484 if (MenuOption
->Skip
> 1) {
2485 Row
+= MenuOption
->Skip
- SkipValue
;
2488 Row
+= MenuOption
->Skip
;
2491 if (Row
> BottomRow
) {
2492 if (!ValueIsScroll (FALSE
, Link
)) {
2496 Row
= BottomRow
+ 1;
2501 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2506 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2508 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2509 TopRow
- SCROLL_ARROW_HEIGHT
,
2513 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2517 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2519 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2520 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2524 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2531 case CfRefreshHighLight
:
2533 // MenuOption: Last menu option that need to remove hilight
2534 // MenuOption is set to NULL in Repaint
2535 // NewPos: Current menu option that need to hilight
2537 ControlFlag
= CfUpdateHelpString
;
2538 if (InitializedFlag
) {
2539 InitializedFlag
= FALSE
;
2540 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2544 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2545 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2547 SavedValue
= Repaint
;
2550 if (Selection
->QuestionId
!= 0) {
2551 NewPos
= gMenuOption
.ForwardLink
;
2552 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2554 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2555 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2556 NewPos
->ForwardLink
!= &gMenuOption
) {
2557 NewPos
= NewPos
->ForwardLink
;
2558 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2560 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2562 // Target Question found, find its MenuOption
2566 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2567 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2568 Index
+= SavedMenuOption
->Skip
;
2569 if (Link
== TopOfScreen
) {
2570 Index
-= OldSkipValue
;
2572 Link
= Link
->ForwardLink
;
2574 if (NewPos
== Link
) {
2575 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2578 if (Link
!= NewPos
|| Index
> BottomRow
|| (Link
== NewPos
&& SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
)) {
2580 // Find the MenuOption which has the skip value for Date/Time opcode.
2582 AdjustDateAndTimePosition(FALSE
, &NewPos
);
2584 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2586 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2588 // SavedMenuOption->Row == 0 means the menu not show yet.
2590 if (SavedMenuOption
->Row
== 0) {
2591 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2595 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2596 Link
= Link
->BackLink
;
2597 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2598 if (SavedMenuOption
->Row
== 0) {
2599 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2601 Index
+= SavedMenuOption
->Skip
;
2604 SkipValue
= Index
- BottomRow
- 1;
2605 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2607 OldSkipValue
= SkipValue
;
2610 TopOfScreen
= Link
->ForwardLink
;
2615 ControlFlag
= CfRepaint
;
2620 // Target Question not found, highlight the default menu option
2622 NewPos
= TopOfScreen
;
2625 Selection
->QuestionId
= 0;
2628 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2629 if (MenuOption
!= NULL
) {
2631 // Remove highlight on last Menu Option
2633 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2634 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2635 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2636 if (OptionString
!= NULL
) {
2637 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2638 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2640 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2643 Width
= (UINT16
) gOptionBlockWidth
;
2644 OriginalRow
= MenuOption
->Row
;
2646 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2647 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2648 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2651 // If there is more string to process print on the next row and increment the Skip value
2653 if (StrLen (&OptionString
[Index
]) != 0) {
2657 FreePool (OutputString
);
2660 MenuOption
->Row
= OriginalRow
;
2662 FreePool (OptionString
);
2665 if (MenuOption
->GrayOut
) {
2666 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2667 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2668 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2671 OriginalRow
= MenuOption
->Row
;
2672 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2674 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2675 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2676 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2679 // If there is more string to process print on the next row and increment the Skip value
2681 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2685 FreePool (OutputString
);
2688 MenuOption
->Row
= OriginalRow
;
2689 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2695 // This is the current selected statement
2697 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2698 Statement
= MenuOption
->ThisTag
;
2699 Selection
->Statement
= Statement
;
2700 if (!IsSelectable (MenuOption
)) {
2701 Repaint
= SavedValue
;
2702 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2707 // Record highlight for current menu
2709 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2710 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2713 // Set reverse attribute
2715 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2716 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2719 // Assuming that we have a refresh linked-list created, lets annotate the
2720 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2721 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2723 if (gMenuRefreshHead
!= NULL
) {
2724 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2725 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2726 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2728 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2730 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2731 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2736 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2737 if (OptionString
!= NULL
) {
2738 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2739 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2741 Width
= (UINT16
) gOptionBlockWidth
;
2743 OriginalRow
= MenuOption
->Row
;
2745 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &Index
, &OutputString
) != 0x0000;) {
2746 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2747 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2750 // If there is more string to process print on the next row and increment the Skip value
2752 if (StrLen (&OptionString
[Index
]) != 0) {
2756 FreePool (OutputString
);
2759 MenuOption
->Row
= OriginalRow
;
2761 FreePool (OptionString
);
2764 OriginalRow
= MenuOption
->Row
;
2766 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2768 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &Index
, &OutputString
) != 0x0000;) {
2769 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2770 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2773 // If there is more string to process print on the next row and increment the Skip value
2775 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2779 FreePool (OutputString
);
2782 MenuOption
->Row
= OriginalRow
;
2787 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2790 // Clear reverse attribute
2792 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2795 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2796 // if we didn't break halfway when process CfRefreshHighLight.
2798 Repaint
= SavedValue
;
2801 case CfUpdateHelpString
:
2802 ControlFlag
= CfPrepareToReadKey
;
2803 if (Selection
->Form
->ModalForm
) {
2807 if (Repaint
|| NewLine
) {
2809 // Don't print anything if it is a NULL help token
2811 ASSERT(MenuOption
!= NULL
);
2812 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
2815 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2818 ProcessHelpString (StringPtr
, &FormattedString
, BottomRow
- TopRow
);
2820 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
2822 for (Index
= 0; Index
< BottomRow
- TopRow
; Index
++) {
2824 // Pad String with spaces to simulate a clearing of the previous line
2826 for (; GetStringWidth (&FormattedString
[Index
* gHelpBlockWidth
* 2]) / 2 < gHelpBlockWidth
;) {
2827 StrCat (&FormattedString
[Index
* gHelpBlockWidth
* 2], L
" ");
2831 LocalScreen
.RightColumn
- gHelpBlockWidth
,
2833 &FormattedString
[Index
* gHelpBlockWidth
* 2]
2838 // Reset this flag every time we finish using it.
2844 case CfPrepareToReadKey
:
2845 ControlFlag
= CfReadKey
;
2846 ScreenOperation
= UiNoOperation
;
2850 ControlFlag
= CfScreenOperation
;
2853 // Wait for user's selection
2856 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
2857 } while (Status
== EFI_TIMEOUT
);
2859 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
2861 // IFR is updated in Callback of refresh opcode, re-parse it
2863 ControlFlag
= CfCheckSelection
;
2864 Selection
->Statement
= NULL
;
2868 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
2870 // If we encounter error, continue to read another key in.
2872 if (EFI_ERROR (Status
)) {
2873 ControlFlag
= CfReadKey
;
2877 switch (Key
.UnicodeChar
) {
2878 case CHAR_CARRIAGE_RETURN
:
2879 if(MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
2880 ControlFlag
= CfReadKey
;
2884 ScreenOperation
= UiSelect
;
2889 // We will push the adjustment of these numeric values directly to the input handler
2890 // NOTE: we won't handle manual input numeric
2895 // If the screen has no menu items, and the user didn't select UiReset
2896 // ignore the selection and go back to reading keys.
2898 if(IsListEmpty (&gMenuOption
) || MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
2899 ControlFlag
= CfReadKey
;
2903 ASSERT(MenuOption
!= NULL
);
2904 Statement
= MenuOption
->ThisTag
;
2905 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
2906 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
2907 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
2909 if (Key
.UnicodeChar
== '+') {
2910 gDirection
= SCAN_RIGHT
;
2912 gDirection
= SCAN_LEFT
;
2914 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
2915 if (EFI_ERROR (Status
)) {
2917 // Repaint to clear possible error prompt pop-up
2922 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2924 if (OptionString
!= NULL
) {
2925 FreePool (OptionString
);
2931 ScreenOperation
= UiUp
;
2936 ScreenOperation
= UiDown
;
2940 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
2942 // If the screen has no menu items, and the user didn't select UiReset
2943 // ignore the selection and go back to reading keys.
2945 if(IsListEmpty (&gMenuOption
)) {
2946 ControlFlag
= CfReadKey
;
2950 ASSERT(MenuOption
!= NULL
);
2951 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
&& !MenuOption
->ReadOnly
) {
2952 ScreenOperation
= UiSelect
;
2958 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
2959 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
2960 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
2965 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
2967 // ModalForm has no ESC key and Hot Key.
2969 ControlFlag
= CfReadKey
;
2970 } else if (Index
== mScanCodeNumber
) {
2972 // Check whether Key matches the registered hot key.
2975 if ((gBrowserSettingScope
== SystemLevel
) || (gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
2976 HotKey
= GetHotKeyFromRegisterList (&Key
);
2978 if (HotKey
!= NULL
) {
2979 ScreenOperation
= UiHotKey
;
2986 case CfScreenOperation
:
2987 if (ScreenOperation
!= UiReset
) {
2989 // If the screen has no menu items, and the user didn't select UiReset
2990 // ignore the selection and go back to reading keys.
2992 if (IsListEmpty (&gMenuOption
)) {
2993 ControlFlag
= CfReadKey
;
2999 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
3002 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
3003 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
3010 ControlFlag
= CfCheckSelection
;
3012 ASSERT(MenuOption
!= NULL
);
3013 Statement
= MenuOption
->ThisTag
;
3014 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
3019 // Keep highlight on current MenuOption
3021 Selection
->QuestionId
= Statement
->QuestionId
;
3023 switch (Statement
->Operand
) {
3024 case EFI_IFR_REF_OP
:
3025 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
3028 case EFI_IFR_ACTION_OP
:
3030 // Process the Config string <ConfigResp>
3032 Status
= ProcessQuestionConfig (Selection
, Statement
);
3034 if (EFI_ERROR (Status
)) {
3039 // The action button may change some Question value, so refresh the form
3041 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3044 case EFI_IFR_RESET_BUTTON_OP
:
3046 // Reset Question to default value specified by DefaultId
3048 ControlFlag
= CfUiDefault
;
3049 DefaultId
= Statement
->DefaultId
;
3054 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3056 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
3057 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3059 if (EFI_ERROR (Status
)) {
3062 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
3064 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3067 if (OptionString
!= NULL
) {
3068 FreePool (OptionString
);
3076 // We come here when someone press ESC
3078 ControlFlag
= CfCheckSelection
;
3079 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3083 ControlFlag
= CfCheckSelection
;
3084 ASSERT(MenuOption
!= NULL
);
3085 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3086 if (MenuOption
->Sequence
!= 0) {
3088 // In the middle or tail of the Date/Time op-code set, go left.
3090 ASSERT(NewPos
!= NULL
);
3091 NewPos
= NewPos
->BackLink
;
3097 ControlFlag
= CfCheckSelection
;
3098 ASSERT(MenuOption
!= NULL
);
3099 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3100 if (MenuOption
->Sequence
!= 2) {
3102 // In the middle or tail of the Date/Time op-code set, go left.
3104 ASSERT(NewPos
!= NULL
);
3105 NewPos
= NewPos
->ForwardLink
;
3111 ControlFlag
= CfCheckSelection
;
3113 SavedListEntry
= NewPos
;
3115 ASSERT(NewPos
!= NULL
);
3117 // Adjust Date/Time position before we advance forward.
3119 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3120 if (NewPos
->BackLink
!= &gMenuOption
) {
3121 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3122 ASSERT (MenuOption
!= NULL
);
3124 NewPos
= NewPos
->BackLink
;
3126 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3127 if (PreviousMenuOption
->Row
== 0) {
3128 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3130 DistanceValue
= PreviousMenuOption
->Skip
;
3132 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3133 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3135 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3137 if (Difference
< 0) {
3139 // We hit the begining MenuOption that can be focused
3140 // so we simply scroll to the top.
3142 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3143 TopOfScreen
= gMenuOption
.ForwardLink
;
3147 // Scroll up to the last page when we have arrived at top page.
3149 NewPos
= &gMenuOption
;
3150 TopOfScreen
= &gMenuOption
;
3151 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3152 ScreenOperation
= UiPageUp
;
3153 ControlFlag
= CfScreenOperation
;
3156 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3158 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3160 TopOfScreen
= NewPos
;
3164 } else if (!IsSelectable (NextMenuOption
)) {
3166 // Continue to go up until scroll to next page or the selectable option is found.
3168 ScreenOperation
= UiUp
;
3169 ControlFlag
= CfScreenOperation
;
3173 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3175 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3176 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3177 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3178 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3181 // Scroll up to the last page.
3183 NewPos
= &gMenuOption
;
3184 TopOfScreen
= &gMenuOption
;
3185 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3186 ScreenOperation
= UiPageUp
;
3187 ControlFlag
= CfScreenOperation
;
3192 ControlFlag
= CfCheckSelection
;
3194 ASSERT(NewPos
!= NULL
);
3195 if (NewPos
->BackLink
== &gMenuOption
) {
3205 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3206 Link
= Link
->BackLink
;
3207 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3208 if (PreviousMenuOption
->Row
== 0) {
3209 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3211 if (Index
< PreviousMenuOption
->Skip
) {
3215 Index
= Index
- PreviousMenuOption
->Skip
;
3218 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3219 if (TopOfScreen
== &gMenuOption
) {
3220 TopOfScreen
= gMenuOption
.ForwardLink
;
3221 NewPos
= gMenuOption
.BackLink
;
3222 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3224 } else if (TopOfScreen
!= Link
) {
3227 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3230 // Finally we know that NewPos is the last MenuOption can be focused.
3234 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3237 if (Index
+ 1 < TopRow
) {
3239 // Back up the previous option.
3241 Link
= Link
->ForwardLink
;
3245 // Move to the option in Next page.
3247 if (TopOfScreen
== &gMenuOption
) {
3248 NewPos
= gMenuOption
.BackLink
;
3249 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3252 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3256 // There are more MenuOption needing scrolling up.
3263 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3264 // Don't do this when we are already in the first page.
3266 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3267 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3271 ControlFlag
= CfCheckSelection
;
3273 ASSERT (NewPos
!= NULL
);
3274 if (NewPos
->ForwardLink
== &gMenuOption
) {
3283 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3285 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
3286 Index
= Index
+ NextMenuOption
->Skip
;
3287 Link
= Link
->ForwardLink
;
3288 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3291 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
)) {
3293 // Finally we know that NewPos is the last MenuOption can be focused.
3296 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3298 if (Index
- 1 > BottomRow
) {
3300 // Back up the previous option.
3302 Link
= Link
->BackLink
;
3305 // There are more MenuOption needing scrolling down.
3310 // Move to the option in Next page.
3312 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3316 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3317 // Don't do this when we are already in the last page.
3320 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3321 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3325 ControlFlag
= CfCheckSelection
;
3327 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3328 // to be one that progresses to the next set of op-codes, we need to advance to the last
3329 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3330 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3331 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3332 // the Date/Time op-code.
3334 SavedListEntry
= NewPos
;
3335 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3337 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3338 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3340 NewPos
= NewPos
->ForwardLink
;
3343 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3344 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3346 // We hit the end of MenuOption that can be focused
3347 // so we simply scroll to the first page.
3349 if (Difference
< 0) {
3351 // Scroll to the first page.
3353 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3354 TopOfScreen
= gMenuOption
.ForwardLink
;
3358 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3360 NewPos
= gMenuOption
.ForwardLink
;
3361 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3364 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3366 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3367 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3371 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3374 // An option might be multi-line, so we need to reflect that data in the overall skip value
3376 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3377 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3379 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3380 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3381 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3382 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3388 // If we are going to scroll, update TopOfScreen
3390 if (Temp
> BottomRow
) {
3393 // Is the current top of screen a zero-advance op-code?
3394 // If so, keep moving forward till we hit a >0 advance op-code
3396 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3399 // If bottom op-code is more than one line or top op-code is more than one line
3401 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3403 // Is the bottom op-code greater than or equal in size to the top op-code?
3405 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3407 // Skip the top op-code
3409 TopOfScreen
= TopOfScreen
->ForwardLink
;
3410 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3412 OldSkipValue
= Difference
;
3414 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3417 // If we have a remainder, skip that many more op-codes until we drain the remainder
3419 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3421 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3423 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3424 TopOfScreen
= TopOfScreen
->ForwardLink
;
3425 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3428 // Since we will act on this op-code in the next routine, and increment the
3429 // SkipValue, set the skips to one less than what is required.
3431 SkipValue
= Difference
- 1;
3435 // Since we will act on this op-code in the next routine, and increment the
3436 // SkipValue, set the skips to one less than what is required.
3438 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3441 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3442 TopOfScreen
= TopOfScreen
->ForwardLink
;
3445 SkipValue
= OldSkipValue
;
3449 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3450 // Let's set a skip flag to smoothly scroll the top of the screen.
3452 if (SavedMenuOption
->Skip
> 1) {
3453 if (SavedMenuOption
== NextMenuOption
) {
3458 } else if (SavedMenuOption
->Skip
== 1) {
3462 TopOfScreen
= TopOfScreen
->ForwardLink
;
3464 } while (SavedMenuOption
->Skip
== 0);
3467 OldSkipValue
= SkipValue
;
3468 } else if (!IsSelectable (NextMenuOption
)) {
3470 // Continue to go down until scroll to next page or the selectable option is found.
3472 ScreenOperation
= UiDown
;
3473 ControlFlag
= CfScreenOperation
;
3476 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3478 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3482 // Scroll to the first page.
3484 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3485 TopOfScreen
= gMenuOption
.ForwardLink
;
3489 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3492 NewPos
= gMenuOption
.ForwardLink
;
3493 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3497 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3499 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3500 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3504 ControlFlag
= CfCheckSelection
;
3506 Status
= EFI_SUCCESS
;
3508 // Discard changes. After it, no NV flag is showed.
3510 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3511 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3512 if (!EFI_ERROR (Status
)) {
3513 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3514 Selection
->Statement
= NULL
;
3515 gResetRequired
= FALSE
;
3518 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3519 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3521 // Still show current page.
3523 Selection
->Action
= UI_ACTION_NONE
;
3531 // Reterieve default setting. After it. NV flag will be showed.
3533 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3534 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
);
3535 if (!EFI_ERROR (Status
)) {
3536 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3537 Selection
->Statement
= NULL
;
3538 gResetRequired
= TRUE
;
3541 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3542 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3544 // Still show current page.
3546 Selection
->Action
= UI_ACTION_NONE
;
3554 // Save changes. After it, no NV flag is showed.
3556 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3557 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3558 if (!EFI_ERROR (Status
)) {
3559 ASSERT(MenuOption
!= NULL
);
3560 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3561 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3564 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3565 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3567 // Still show current page.
3569 Selection
->Action
= UI_ACTION_NONE
;
3577 // Set Reset required Flag
3579 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3580 gResetRequired
= TRUE
;
3586 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3588 // Form Exit without saving, Similar to ESC Key.
3589 // FormSet Exit without saving, Exit SendForm.
3590 // System Exit without saving, CallExitHandler and Exit SendForm.
3592 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3593 if (gBrowserSettingScope
== FormLevel
) {
3594 ControlFlag
= CfUiReset
;
3595 } else if (gBrowserSettingScope
== FormSetLevel
) {
3596 Selection
->Action
= UI_ACTION_EXIT
;
3597 } else if (gBrowserSettingScope
== SystemLevel
) {
3598 if (ExitHandlerFunction
!= NULL
) {
3599 ExitHandlerFunction ();
3601 Selection
->Action
= UI_ACTION_EXIT
;
3603 Selection
->Statement
= NULL
;
3608 ControlFlag
= CfCheckSelection
;
3610 // Reset to default value for all forms in the whole system.
3612 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
);
3614 if (!EFI_ERROR (Status
)) {
3615 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3616 Selection
->Statement
= NULL
;
3617 gResetRequired
= TRUE
;
3621 case CfUiNoOperation
:
3622 ControlFlag
= CfCheckSelection
;
3626 UiFreeRefreshList ();
3628 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3629 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3630 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3631 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");