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
, GetSetValueWithHiiDriver
);
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
;
771 // break skipped on purpose
774 MenuOption
->IsQuestion
= FALSE
;
778 if ((Statement
->ValueExpression
!= NULL
) ||
779 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
780 MenuOption
->ReadOnly
= TRUE
;
783 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
791 Routine used to abstract a generic dialog interface and return the selected key or string
793 @param NumberOfLines The number of lines for the dialog box
794 @param HotKey Defines whether a single character is parsed
795 (TRUE) and returned in KeyValue or a string is
796 returned in StringBuffer. Two special characters
797 are considered when entering a string, a SCAN_ESC
798 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
799 string input and returns
800 @param MaximumStringSize The maximum size in bytes of a typed in string
801 (each character is a CHAR16) and the minimum
802 string returned is two bytes
803 @param StringBuffer The passed in pointer to the buffer which will
804 hold the typed in string if HotKey is FALSE
805 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
806 @param ... A series of (quantity == NumberOfLines) text
807 strings which will be used to construct the dialog
810 @retval EFI_SUCCESS Displayed dialog and received user interaction
811 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
812 (StringBuffer == NULL) && (HotKey == FALSE))
813 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
819 IN UINTN NumberOfLines
,
821 IN UINTN MaximumStringSize
,
822 OUT CHAR16
*StringBuffer
,
823 OUT EFI_INPUT_KEY
*KeyValue
,
832 CHAR16
*BufferedString
;
839 BOOLEAN SelectionComplete
;
841 UINTN CurrentAttribute
;
842 UINTN DimensionsWidth
;
843 UINTN DimensionsHeight
;
845 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
846 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
848 SelectionComplete
= FALSE
;
850 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
851 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
852 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
855 ASSERT (BufferedString
);
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
);
878 VA_START (Marker
, KeyValue
);
881 // Determine the largest string in the dialog box
882 // Notice we are starting with 1 since String is the first string
884 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
885 StackString
= VA_ARG (Marker
, CHAR16
*);
887 if (StackString
[0] == L
' ') {
888 InputOffset
= Count
+ 1;
891 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
893 // Size of the string visually and subtract the width by one for the null-terminator
895 LargestString
= (GetStringWidth (StackString
) / 2);
900 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
901 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
908 VA_START (Marker
, KeyValue
);
909 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
913 // Take the first key typed and report it back?
916 Status
= WaitForKeyStroke (&Key
);
917 ASSERT_EFI_ERROR (Status
);
918 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
922 Status
= WaitForKeyStroke (&Key
);
924 switch (Key
.UnicodeChar
) {
926 switch (Key
.ScanCode
) {
928 FreePool (TempString
);
929 FreePool (BufferedString
);
930 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
931 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
932 return EFI_DEVICE_ERROR
;
940 case CHAR_CARRIAGE_RETURN
:
941 SelectionComplete
= TRUE
;
942 FreePool (TempString
);
943 FreePool (BufferedString
);
944 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
945 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
950 if (StringBuffer
[0] != CHAR_NULL
) {
951 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
952 TempString
[Index
] = StringBuffer
[Index
];
955 // Effectively truncate string by 1 character
957 TempString
[Index
- 1] = CHAR_NULL
;
958 StrCpy (StringBuffer
, TempString
);
961 // break skipped on purpose
966 // If it is the beginning of the string, don't worry about checking maximum limits
968 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
969 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
970 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
971 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
972 KeyPad
[0] = Key
.UnicodeChar
;
973 KeyPad
[1] = CHAR_NULL
;
974 StrCat (StringBuffer
, KeyPad
);
975 StrCat (TempString
, KeyPad
);
978 // If the width of the input string is now larger than the screen, we nee to
979 // adjust the index to start printing portions of the string
981 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
983 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
985 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
986 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
991 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
992 BufferedString
[Count
] = StringBuffer
[Index
];
995 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
998 } while (!SelectionComplete
);
1001 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
1002 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1007 Draw a pop up windows based on the dimension, number of lines and
1010 @param RequestedWidth The width of the pop-up.
1011 @param NumberOfLines The number of lines.
1012 @param Marker The variable argument list for the list of string to be printed.
1017 IN UINTN RequestedWidth
,
1018 IN UINTN NumberOfLines
,
1030 UINTN DimensionsWidth
;
1031 UINTN DimensionsHeight
;
1033 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
1034 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
1036 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1038 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
1039 RequestedWidth
= DimensionsWidth
- 2;
1043 // Subtract the PopUp width from total Columns, allow for one space extra on
1044 // each end plus a border.
1046 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
1047 End
= Start
+ RequestedWidth
+ 1;
1049 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
1050 Bottom
= Top
+ NumberOfLines
+ 2;
1052 Character
= BOXDRAW_DOWN_RIGHT
;
1053 PrintCharAt (Start
, Top
, Character
);
1054 Character
= BOXDRAW_HORIZONTAL
;
1055 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1056 PrintChar (Character
);
1059 Character
= BOXDRAW_DOWN_LEFT
;
1060 PrintChar (Character
);
1061 Character
= BOXDRAW_VERTICAL
;
1064 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
1065 String
= VA_ARG (Marker
, CHAR16
*);
1068 // This will clear the background of the line - we never know who might have been
1069 // here before us. This differs from the next clear in that it used the non-reverse
1070 // video for normal printing.
1072 if (GetStringWidth (String
) / 2 > 1) {
1073 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1077 // Passing in a space results in the assumption that this is where typing will occur
1079 if (String
[0] == L
' ') {
1080 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
1084 // Passing in a NULL results in a blank space
1086 if (String
[0] == CHAR_NULL
) {
1087 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1091 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
1095 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1096 PrintCharAt (Start
, Index
+ 1, Character
);
1097 PrintCharAt (End
- 1, Index
+ 1, Character
);
1100 Character
= BOXDRAW_UP_RIGHT
;
1101 PrintCharAt (Start
, Bottom
- 1, Character
);
1102 Character
= BOXDRAW_HORIZONTAL
;
1103 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1104 PrintChar (Character
);
1107 Character
= BOXDRAW_UP_LEFT
;
1108 PrintChar (Character
);
1112 Draw a pop up windows based on the dimension, number of lines and
1115 @param RequestedWidth The width of the pop-up.
1116 @param NumberOfLines The number of lines.
1117 @param ... A series of text strings that displayed in the pop-up.
1122 CreateMultiStringPopUp (
1123 IN UINTN RequestedWidth
,
1124 IN UINTN NumberOfLines
,
1130 VA_START (Marker
, NumberOfLines
);
1132 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1139 Update status bar on the bottom of menu.
1141 @param Selection Current Selction info.
1142 @param MessageType The type of message to be shown.
1143 @param Flags The flags in Question header.
1144 @param State Set or clear.
1149 IN UI_MENU_SELECTION
*Selection
,
1150 IN UINTN MessageType
,
1156 CHAR16
*NvUpdateMessage
;
1157 CHAR16
*InputErrorMessage
;
1159 FORM_BROWSER_FORMSET
*LocalFormSet
;
1160 FORM_BROWSER_STATEMENT
*Question
;
1162 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1163 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1165 switch (MessageType
) {
1168 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1170 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1171 gScreenDimensions
.BottomRow
- 1,
1176 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1177 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1178 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1181 mInputError
= FALSE
;
1185 case NV_UPDATE_REQUIRED
:
1187 // Global setting support. Show configuration change on every form.
1190 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1192 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1193 Question
= Selection
->Statement
;
1194 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1196 // Update only for Question value that need to be saved into Storage.
1198 Selection
->Form
->NvUpdateRequired
= TRUE
;
1202 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1203 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1205 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1206 gScreenDimensions
.BottomRow
- 1,
1211 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1212 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1214 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1215 gScreenDimensions
.BottomRow
- 1,
1222 case REFRESH_STATUS_BAR
:
1224 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1227 switch (gBrowserSettingScope
) {
1230 // Check the maintain list to see whether there is any change.
1232 Link
= GetFirstNode (&gBrowserFormSetList
);
1233 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1234 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1235 if (IsNvUpdateRequired(LocalFormSet
)) {
1236 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1239 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1244 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1255 FreePool (InputErrorMessage
);
1256 FreePool (NvUpdateMessage
);
1262 Get the supported width for a particular op-code
1264 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1265 @param Handle The handle in the HII database being used
1267 @return Returns the number of CHAR16 characters that is support.
1272 IN FORM_BROWSER_STATEMENT
*Statement
,
1273 IN EFI_HII_HANDLE Handle
1283 // See if the second text parameter is really NULL
1285 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1286 String
= GetToken (Statement
->TextTwo
, Handle
);
1287 Size
= StrLen (String
);
1291 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1292 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1293 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1294 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1295 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1297 // Allow a wide display if text op-code and no secondary text op-code
1299 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1301 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1303 Width
= (UINT16
) gPromptBlockWidth
;
1306 if (Statement
->InSubtitle
) {
1307 Width
-= SUBTITLE_INDENT
;
1310 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1314 Will copy LineWidth amount of a string in the OutputString buffer and return the
1315 number of CHAR16 characters that were copied into the OutputString buffer.
1316 The output string format is:
1317 Glyph Info + String info + '\0'.
1319 In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
1321 @param InputString String description for this option.
1322 @param LineWidth Width of the desired string to extract in CHAR16
1324 @param GlyphWidth The glyph width of the begin of the char in the string.
1325 @param Index Where in InputString to start the copy process
1326 @param OutputString Buffer to copy the string into
1328 @return Returns the number of CHAR16 characters that were copied into the OutputString
1329 buffer, include extra glyph info and '\0' info.
1334 IN CHAR16
*InputString
,
1335 IN UINT16 LineWidth
,
1336 IN OUT UINT16
*GlyphWidth
,
1337 IN OUT UINTN
*Index
,
1338 OUT CHAR16
**OutputString
1343 UINT16 OriginalGlyphWidth
;
1345 UINT16 LastSpaceOffset
;
1346 UINT16 LastGlyphWidth
;
1348 if (InputString
== NULL
|| Index
== NULL
|| OutputString
== NULL
) {
1352 if (LineWidth
== 0 || *GlyphWidth
== 0) {
1357 // Save original glyph width.
1359 OriginalGlyphWidth
= *GlyphWidth
;
1360 LastGlyphWidth
= OriginalGlyphWidth
;
1362 LastSpaceOffset
= 0;
1365 // 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.
1366 // To avoid displaying this empty line in screen, just skip the two CHARs here.
1368 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1369 *Index
= *Index
+ 2;
1373 // Fast-forward the string and see if there is a carriage-return in the string
1375 for (StrOffset
= 0, GlyphOffset
= 0; GlyphOffset
<= LineWidth
; StrOffset
++) {
1376 switch (InputString
[*Index
+ StrOffset
]) {
1385 case CHAR_CARRIAGE_RETURN
:
1392 GlyphOffset
= GlyphOffset
+ *GlyphWidth
;
1395 // Record the last space info in this line. Will be used in rewind.
1397 if ((InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) && (GlyphOffset
<= LineWidth
)) {
1398 LastSpaceOffset
= StrOffset
;
1399 LastGlyphWidth
= *GlyphWidth
;
1410 // Rewind the string from the maximum size until we see a space to break the line
1412 if (GlyphOffset
> LineWidth
) {
1414 // Rewind the string to last space char in this line.
1416 if (LastSpaceOffset
!= 0) {
1417 StrOffset
= LastSpaceOffset
;
1418 *GlyphWidth
= LastGlyphWidth
;
1421 // Roll back to last char in the line width.
1428 // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
1430 if (StrOffset
== 0 && (InputString
[*Index
+ StrOffset
] == CHAR_NULL
)) {
1435 // Need extra glyph info and '\0' info, so +2.
1437 *OutputString
= AllocateZeroPool (((UINTN
) (StrOffset
+ 2) * sizeof(CHAR16
)));
1438 if (*OutputString
== NULL
) {
1443 // Save the glyph info at the begin of the string, will used by Print function.
1445 if (OriginalGlyphWidth
== 1) {
1446 *(*OutputString
) = NARROW_CHAR
;
1448 *(*OutputString
) = WIDE_CHAR
;
1451 CopyMem ((*OutputString
) + 1, &InputString
[*Index
], StrOffset
* sizeof(CHAR16
));
1453 if (InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) {
1455 // Skip the space info at the begin of next line.
1457 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1458 } else if ((InputString
[*Index
+ StrOffset
] == CHAR_LINEFEED
)) {
1460 // Skip the /n or /n/r info.
1462 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_CARRIAGE_RETURN
) {
1463 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1465 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1467 } else if ((InputString
[*Index
+ StrOffset
] == CHAR_CARRIAGE_RETURN
)) {
1469 // Skip the /r or /r/n info.
1471 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_LINEFEED
) {
1472 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1474 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1477 *Index
= (UINT16
) (*Index
+ StrOffset
);
1481 // Include extra glyph info and '\0' info, so +2.
1483 return StrOffset
+ 2;
1488 Update display lines for a Menu Option.
1490 @param Selection The user's selection.
1491 @param MenuOption The MenuOption to be checked.
1495 UpdateOptionSkipLines (
1496 IN UI_MENU_SELECTION
*Selection
,
1497 IN UI_MENU_OPTION
*MenuOption
1504 CHAR16
*OutputString
;
1505 CHAR16
*OptionString
;
1509 OptionString
= NULL
;
1510 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1512 if (OptionString
!= NULL
) {
1513 Width
= (UINT16
) gOptionBlockWidth
;
1518 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
1520 // If there is more string to process print on the next row and increment the Skip value
1522 if (StrLen (&OptionString
[Index
]) != 0) {
1525 // Since the Number of lines for this menu entry may or may not be reflected accurately
1526 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1527 // some testing to ensure we are keeping this in-sync.
1529 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1531 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1536 FreePool (OutputString
);
1542 if (OptionString
!= NULL
) {
1543 FreePool (OptionString
);
1549 Check whether this Menu Option could be highlighted.
1551 This is an internal function.
1553 @param MenuOption The MenuOption to be checked.
1555 @retval TRUE This Menu Option is selectable.
1556 @retval FALSE This Menu Option could not be selected.
1561 UI_MENU_OPTION
*MenuOption
1564 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1565 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1574 Determine if the menu is the last menu that can be selected.
1576 This is an internal function.
1578 @param Direction The scroll direction. False is down. True is up.
1579 @param CurrentPos The current focus.
1581 @return FALSE -- the menu isn't the last menu that can be selected.
1582 @return TRUE -- the menu is the last menu that can be selected.
1587 IN BOOLEAN Direction
,
1588 IN LIST_ENTRY
*CurrentPos
1593 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1595 if (Temp
== &gMenuOption
) {
1604 Move to next selectable statement.
1606 This is an internal function.
1608 @param Selection Menu selection.
1609 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1610 @param CurrentPosition Current position.
1611 @param GapToTop Gap position to top or bottom.
1613 @return The row distance from current MenuOption to next selectable MenuOption.
1617 MoveToNextStatement (
1618 IN UI_MENU_SELECTION
*Selection
,
1620 IN OUT LIST_ENTRY
**CurrentPosition
,
1626 UI_MENU_OPTION
*NextMenuOption
;
1627 UI_MENU_OPTION
*PreMenuOption
;
1630 Pos
= *CurrentPosition
;
1631 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1634 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1635 if (NextMenuOption
->Row
== 0) {
1636 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1639 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1641 // Current Position doesn't need to be caculated when go up.
1642 // Caculate distanct at first when go up
1644 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1645 NextMenuOption
= PreMenuOption
;
1648 Distance
+= NextMenuOption
->Skip
;
1650 if (IsSelectable (NextMenuOption
)) {
1653 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1662 // Caculate distanct at later when go down
1664 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1665 NextMenuOption
= PreMenuOption
;
1668 Distance
+= NextMenuOption
->Skip
;
1670 PreMenuOption
= NextMenuOption
;
1671 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1674 *CurrentPosition
= &NextMenuOption
->Link
;
1680 Adjust Data and Time position accordingly.
1681 Data format : [01/02/2004] [11:22:33]
1682 Line number : 0 0 1 0 0 1
1684 This is an internal function.
1686 @param DirectionUp the up or down direction. False is down. True is
1688 @param CurrentPosition Current position. On return: Point to the last
1689 Option (Year or Second) if up; Point to the first
1690 Option (Month or Hour) if down.
1692 @return Return line number to pad. It is possible that we stand on a zero-advance
1693 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1697 AdjustDateAndTimePosition (
1698 IN BOOLEAN DirectionUp
,
1699 IN OUT LIST_ENTRY
**CurrentPosition
1703 LIST_ENTRY
*NewPosition
;
1704 UI_MENU_OPTION
*MenuOption
;
1705 UINTN PadLineNumber
;
1708 NewPosition
= *CurrentPosition
;
1709 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1711 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1712 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1714 // Calculate the distance from current position to the last Date/Time MenuOption
1717 while (MenuOption
->Skip
== 0) {
1719 NewPosition
= NewPosition
->ForwardLink
;
1720 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1724 NewPosition
= *CurrentPosition
;
1727 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1728 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1729 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1730 // checking can be done.
1732 while (Count
++ < 2) {
1733 NewPosition
= NewPosition
->BackLink
;
1737 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1738 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1739 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1740 // checking can be done.
1742 while (Count
-- > 0) {
1743 NewPosition
= NewPosition
->ForwardLink
;
1747 *CurrentPosition
= NewPosition
;
1750 return PadLineNumber
;
1754 Find HII Handle in the HII database associated with given Device Path.
1756 If DevicePath is NULL, then ASSERT.
1758 @param DevicePath Device Path associated with the HII package list
1761 @retval Handle HII package list Handle associated with the Device
1763 @retval NULL Hii Package list handle is not found.
1768 DevicePathToHiiHandle (
1769 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1773 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1778 EFI_HANDLE DriverHandle
;
1779 EFI_HII_HANDLE
*HiiHandles
;
1780 EFI_HII_HANDLE HiiHandle
;
1782 ASSERT (DevicePath
!= NULL
);
1784 TmpDevicePath
= DevicePath
;
1786 // Locate Device Path Protocol handle buffer
1788 Status
= gBS
->LocateDevicePath (
1789 &gEfiDevicePathProtocolGuid
,
1793 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1798 // Retrieve all HII Handles from HII database
1800 BufferSize
= 0x1000;
1801 HiiHandles
= AllocatePool (BufferSize
);
1802 ASSERT (HiiHandles
!= NULL
);
1803 Status
= mHiiDatabase
->ListPackageLists (
1805 EFI_HII_PACKAGE_TYPE_ALL
,
1810 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1811 FreePool (HiiHandles
);
1812 HiiHandles
= AllocatePool (BufferSize
);
1813 ASSERT (HiiHandles
!= NULL
);
1815 Status
= mHiiDatabase
->ListPackageLists (
1817 EFI_HII_PACKAGE_TYPE_ALL
,
1824 if (EFI_ERROR (Status
)) {
1825 FreePool (HiiHandles
);
1830 // Search Hii Handle by Driver Handle
1833 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1834 for (Index
= 0; Index
< HandleCount
; Index
++) {
1835 Status
= mHiiDatabase
->GetPackageListHandle (
1840 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1841 HiiHandle
= HiiHandles
[Index
];
1846 FreePool (HiiHandles
);
1851 Find HII Handle in the HII database associated with given form set guid.
1853 If FormSetGuid is NULL, then ASSERT.
1855 @param ComparingGuid FormSet Guid associated with the HII package list
1858 @retval Handle HII package list Handle associated with the Device
1860 @retval NULL Hii Package list handle is not found.
1864 FormSetGuidToHiiHandle (
1865 EFI_GUID
*ComparingGuid
1868 EFI_HII_HANDLE
*HiiHandles
;
1870 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1874 UINT32 PackageListLength
;
1875 EFI_HII_PACKAGE_HEADER PackageHeader
;
1879 EFI_HII_HANDLE HiiHandle
;
1881 ASSERT (ComparingGuid
!= NULL
);
1885 // Get all the Hii handles
1887 HiiHandles
= HiiGetHiiHandles (NULL
);
1888 ASSERT (HiiHandles
!= NULL
);
1891 // Search for formset of each class type
1893 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1895 HiiPackageList
= NULL
;
1896 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1897 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1898 HiiPackageList
= AllocatePool (BufferSize
);
1899 ASSERT (HiiPackageList
!= NULL
);
1901 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1903 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1908 // Get Form package from this HII package List
1910 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1912 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1914 while (Offset
< PackageListLength
) {
1915 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1916 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1918 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1920 // Search FormSet in this Form Package
1922 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1923 while (Offset2
< PackageHeader
.Length
) {
1924 OpCodeData
= Package
+ Offset2
;
1926 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1928 // Try to compare against formset GUID
1930 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1931 HiiHandle
= HiiHandles
[Index
];
1936 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1939 if (HiiHandle
!= NULL
) {
1942 Offset
+= PackageHeader
.Length
;
1945 FreePool (HiiPackageList
);
1946 if (HiiHandle
!= NULL
) {
1951 FreePool (HiiHandles
);
1957 Transfer the device path string to binary format.
1959 @param StringPtr The device path string info.
1961 @retval Device path binary info.
1964 EFI_DEVICE_PATH_PROTOCOL
*
1965 ConvertDevicePathFromText (
1966 IN CHAR16
*StringPtr
1970 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1972 UINT8
*DevicePathBuffer
;
1976 ASSERT (StringPtr
!= NULL
);
1978 BufferSize
= StrLen (StringPtr
) / 2;
1979 DevicePath
= AllocatePool (BufferSize
);
1980 ASSERT (DevicePath
!= NULL
);
1983 // Convert from Device Path String to DevicePath Buffer in the reverse order.
1985 DevicePathBuffer
= (UINT8
*) DevicePath
;
1986 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
1987 TemStr
[0] = StringPtr
[Index
];
1988 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
1989 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
1991 // Invalid Hex Char as the tail.
1995 if ((Index
& 1) == 0) {
1996 DevicePathBuffer
[Index
/2] = DigitUint8
;
1998 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2006 Process the goto op code, update the info in the selection structure.
2008 @param Statement The statement belong to goto op code.
2009 @param Selection The selection info.
2010 @param Repaint Whether need to repaint the menu.
2011 @param NewLine Whether need to create new line.
2013 @retval EFI_SUCCESS The menu process successfully.
2014 @return Other value if the process failed.
2018 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
2019 IN OUT UI_MENU_SELECTION
*Selection
,
2020 OUT BOOLEAN
*Repaint
,
2021 OUT BOOLEAN
*NewLine
2025 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
2026 FORM_BROWSER_FORM
*RefForm
;
2029 UI_MENU_LIST
*MenuList
;
2030 BOOLEAN UpdateFormInfo
;
2032 Status
= EFI_SUCCESS
;
2033 UpdateFormInfo
= TRUE
;
2037 // Prepare the device path check, get the device path info first.
2039 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
2040 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
2044 // Check whether the device path string is a valid string.
2046 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0 && StringPtr
!= NULL
) {
2047 if (Selection
->Form
->ModalForm
) {
2051 // Goto another Hii Package list
2053 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2054 DevicePath
= ConvertDevicePathFromText (StringPtr
);
2056 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2057 FreePool (DevicePath
);
2058 FreePool (StringPtr
);
2060 if (Selection
->Handle
== NULL
) {
2062 // If target Hii Handle not found, exit
2064 Selection
->Action
= UI_ACTION_EXIT
;
2065 Selection
->Statement
= NULL
;
2069 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2070 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2071 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2072 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
2073 if (Selection
->Form
->ModalForm
) {
2077 // Goto another Formset, check for uncommitted data
2079 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2081 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
2082 if (Selection
->Handle
== NULL
) {
2084 // If target Hii Handle not found, exit
2086 Selection
->Action
= UI_ACTION_EXIT
;
2087 Selection
->Statement
= NULL
;
2091 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2092 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2093 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2094 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
2096 // Check whether target From is suppressed.
2098 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
2100 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2101 if (EvaluateExpressionList(RefForm
->SuppressExpression
, TRUE
, Selection
->FormSet
, RefForm
) != ExpressFalse
) {
2103 // Form is suppressed.
2106 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2107 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2108 if (Repaint
!= NULL
) {
2116 // Goto another form inside this formset,
2118 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2120 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2121 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2122 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
2124 // Goto another Question
2126 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2128 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2129 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2131 if (Repaint
!= NULL
) {
2134 if (NewLine
!= NULL
) {
2138 UpdateFormInfo
= FALSE
;
2140 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2141 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2143 UpdateFormInfo
= FALSE
;
2146 if (UpdateFormInfo
) {
2148 // Link current form so that we can always go back when someone hits the ESC
2150 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2151 if (MenuList
== NULL
&& Selection
->CurrentMenu
!= NULL
) {
2152 MenuList
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2160 Display menu and wait for user to select one menu option, then return it.
2161 If AutoBoot is enabled, then if user doesn't select any option,
2162 after period of time, it will automatically return the first menu option.
2164 @param Selection Menu selection.
2166 @retval EFI_SUCESSS This function always return successfully for now.
2171 IN OUT UI_MENU_SELECTION
*Selection
2177 UINTN DistanceValue
;
2188 CHAR16
*OptionString
;
2189 CHAR16
*OutputString
;
2191 CHAR16
*HelpHeaderString
;
2192 CHAR16
*HelpBottomString
;
2198 BOOLEAN InitializedFlag
;
2203 LIST_ENTRY
*TopOfScreen
;
2204 LIST_ENTRY
*SavedListEntry
;
2205 UI_MENU_OPTION
*MenuOption
;
2206 UI_MENU_OPTION
*NextMenuOption
;
2207 UI_MENU_OPTION
*SavedMenuOption
;
2208 UI_MENU_OPTION
*PreviousMenuOption
;
2209 UI_CONTROL_FLAG ControlFlag
;
2210 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2211 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2212 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2213 UI_SCREEN_OPERATION ScreenOperation
;
2214 UINT8 MinRefreshInterval
;
2216 FORM_BROWSER_STATEMENT
*Statement
;
2217 UI_MENU_LIST
*CurrentMenu
;
2218 UINTN ModalSkipColumn
;
2219 BROWSER_HOT_KEY
*HotKey
;
2220 UINTN HelpPageIndex
;
2221 UINTN HelpPageCount
;
2224 UINTN HelpHeaderLine
;
2225 UINTN HelpBottomLine
;
2226 BOOLEAN MultiHelpPage
;
2228 UINT16 EachLineWidth
;
2229 UINT16 HeaderLineWidth
;
2230 UINT16 BottomLineWidth
;
2232 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2234 Status
= EFI_SUCCESS
;
2236 HelpHeaderString
= NULL
;
2237 HelpBottomString
= NULL
;
2238 OptionString
= NULL
;
2239 ScreenOperation
= UiNoOperation
;
2241 MinRefreshInterval
= 0;
2249 MultiHelpPage
= FALSE
;
2251 HeaderLineWidth
= 0;
2252 BottomLineWidth
= 0;
2253 OutputString
= NULL
;
2258 MenuRefreshEntry
= gMenuRefreshHead
;
2260 NextMenuOption
= NULL
;
2261 PreviousMenuOption
= NULL
;
2262 SavedMenuOption
= NULL
;
2264 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2266 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2268 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2269 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2270 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2272 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2273 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2276 if (Selection
->Form
->ModalForm
) {
2277 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2279 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2282 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2284 Selection
->TopRow
= TopRow
;
2285 Selection
->BottomRow
= BottomRow
;
2286 Selection
->PromptCol
= Col
;
2287 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2288 Selection
->Statement
= NULL
;
2290 TopOfScreen
= gMenuOption
.ForwardLink
;
2295 // Find current Menu
2297 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2298 if (CurrentMenu
== NULL
) {
2300 // Current menu not found, add it to the menu tree
2302 CurrentMenu
= UiAddMenuList (NULL
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2304 ASSERT (CurrentMenu
!= NULL
);
2305 Selection
->CurrentMenu
= CurrentMenu
;
2307 if (Selection
->QuestionId
== 0) {
2309 // Highlight not specified, fetch it from cached menu
2311 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2312 Selection
->Sequence
= CurrentMenu
->Sequence
;
2316 // Init option as the current user's selection
2318 InitializedFlag
= TRUE
;
2319 NewPos
= gMenuOption
.ForwardLink
;
2321 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2322 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2324 ControlFlag
= CfInitialization
;
2325 Selection
->Action
= UI_ACTION_NONE
;
2327 switch (ControlFlag
) {
2328 case CfInitialization
:
2329 if (IsListEmpty (&gMenuOption
)) {
2330 ControlFlag
= CfReadKey
;
2332 ControlFlag
= CfCheckSelection
;
2336 case CfCheckSelection
:
2337 if (Selection
->Action
!= UI_ACTION_NONE
) {
2338 ControlFlag
= CfExit
;
2340 ControlFlag
= CfRepaint
;
2345 ControlFlag
= CfRefreshHighLight
;
2355 Temp
= (UINTN
) SkipValue
;
2356 Temp2
= (UINTN
) SkipValue
;
2358 if (Selection
->Form
->ModalForm
) {
2360 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2361 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2362 TopRow
- SCROLL_ARROW_HEIGHT
,
2363 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2364 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2368 LocalScreen
.LeftColumn
,
2369 LocalScreen
.RightColumn
,
2370 TopRow
- SCROLL_ARROW_HEIGHT
,
2371 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2372 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2375 UiFreeRefreshList ();
2376 MinRefreshInterval
= 0;
2378 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2379 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2380 MenuOption
->Row
= Row
;
2381 MenuOption
->Col
= Col
;
2382 if (Selection
->Form
->ModalForm
) {
2383 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2385 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2388 Statement
= MenuOption
->ThisTag
;
2389 if (Statement
->InSubtitle
) {
2390 MenuOption
->Col
+= SUBTITLE_INDENT
;
2393 if (MenuOption
->GrayOut
) {
2394 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2396 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2397 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2401 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2405 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2407 // Print Arrow for Goto button.
2410 MenuOption
->Col
- 2,
2413 GEOMETRICSHAPE_RIGHT_TRIANGLE
2417 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2418 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2419 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2422 // If there is more string to process print on the next row and increment the Skip value
2424 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2430 FreePool (OutputString
);
2439 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2440 if (EFI_ERROR (Status
)) {
2442 // Repaint to clear possible error prompt pop-up
2446 ControlFlag
= CfRepaint
;
2450 if (OptionString
!= NULL
) {
2451 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2452 ProcessStringForDateTime(MenuOption
, OptionString
, TRUE
);
2455 Width
= (UINT16
) gOptionBlockWidth
;
2459 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2460 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2461 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2464 // If there is more string to process print on the next row and increment the Skip value
2466 if (StrLen (&OptionString
[Index
]) != 0) {
2470 // Since the Number of lines for this menu entry may or may not be reflected accurately
2471 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2472 // some testing to ensure we are keeping this in-sync.
2474 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2476 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2482 FreePool (OutputString
);
2491 FreePool (OptionString
);
2495 // If Question has refresh guid, register the op-code.
2497 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2498 if (gMenuEventGuidRefreshHead
== NULL
) {
2499 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2500 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2502 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2503 while (MenuUpdateEntry
->Next
!= NULL
) {
2504 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2506 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2507 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2509 ASSERT (MenuUpdateEntry
!= NULL
);
2510 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2511 ASSERT (!EFI_ERROR (Status
));
2512 MenuUpdateEntry
->MenuOption
= MenuOption
;
2513 MenuUpdateEntry
->Selection
= Selection
;
2514 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2515 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2516 if (MenuOption
->GrayOut
) {
2517 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2519 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2524 // If Question request refresh, register the op-code
2526 if (Statement
->RefreshInterval
!= 0) {
2528 // Menu will be refreshed at minimal interval of all Questions
2529 // which have refresh request
2531 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2532 MinRefreshInterval
= Statement
->RefreshInterval
;
2535 if (gMenuRefreshHead
== NULL
) {
2536 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2537 gMenuRefreshHead
= MenuRefreshEntry
;
2539 MenuRefreshEntry
= gMenuRefreshHead
;
2540 while (MenuRefreshEntry
->Next
!= NULL
) {
2541 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2543 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2544 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2546 ASSERT (MenuRefreshEntry
!= NULL
);
2547 MenuRefreshEntry
->MenuOption
= MenuOption
;
2548 MenuRefreshEntry
->Selection
= Selection
;
2549 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2550 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2551 if (MenuOption
->GrayOut
) {
2552 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2554 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2559 // If this is a text op with secondary text information
2561 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2562 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2564 Width
= (UINT16
) gOptionBlockWidth
;
2568 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2569 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2570 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2573 // If there is more string to process print on the next row and increment the Skip value
2575 if (StrLen (&StringPtr
[Index
]) != 0) {
2579 // Since the Number of lines for this menu entry may or may not be reflected accurately
2580 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2581 // some testing to ensure we are keeping this in-sync.
2583 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2585 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2591 FreePool (OutputString
);
2598 FreePool (StringPtr
);
2600 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2603 // Need to handle the bottom of the display
2605 if (MenuOption
->Skip
> 1) {
2606 Row
+= MenuOption
->Skip
- SkipValue
;
2609 Row
+= MenuOption
->Skip
;
2612 if (Row
> BottomRow
) {
2613 if (!ValueIsScroll (FALSE
, Link
)) {
2617 Row
= BottomRow
+ 1;
2622 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2627 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2629 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2630 TopRow
- SCROLL_ARROW_HEIGHT
,
2634 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2638 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2640 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2641 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2645 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2652 case CfRefreshHighLight
:
2654 // MenuOption: Last menu option that need to remove hilight
2655 // MenuOption is set to NULL in Repaint
2656 // NewPos: Current menu option that need to hilight
2658 ControlFlag
= CfUpdateHelpString
;
2659 if (InitializedFlag
) {
2660 InitializedFlag
= FALSE
;
2661 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2665 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2666 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2668 SavedValue
= Repaint
;
2671 if (Selection
->QuestionId
!= 0) {
2672 NewPos
= gMenuOption
.ForwardLink
;
2673 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2675 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2676 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2677 NewPos
->ForwardLink
!= &gMenuOption
) {
2678 NewPos
= NewPos
->ForwardLink
;
2679 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2681 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2683 // Target Question found, find its MenuOption
2687 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2688 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2689 Index
+= SavedMenuOption
->Skip
;
2690 if (Link
== TopOfScreen
) {
2691 Index
-= OldSkipValue
;
2693 Link
= Link
->ForwardLink
;
2695 if (NewPos
== Link
) {
2696 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2700 // Not find the selected menu in current show page.
2701 // Have two case to enter this if:
2702 // 1. Not find the menu at current page.
2703 // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
2704 // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
2706 // Base on the selected menu will show at the bottom of the page,
2707 // select the menu which will show at the top of the page.
2709 if (Link
!= NewPos
|| Index
> BottomRow
||
2710 (Link
== NewPos
&& (SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
) && (Link
!= TopOfScreen
))) {
2712 // Find the MenuOption which has the skip value for Date/Time opcode.
2714 AdjustDateAndTimePosition(FALSE
, &NewPos
);
2716 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2718 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2720 // SavedMenuOption->Row == 0 means the menu not show yet.
2722 if (SavedMenuOption
->Row
== 0) {
2723 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2727 // Base on the selected menu will show at the bottome of next page,
2728 // select the menu show at the top of the next page.
2731 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2732 Link
= Link
->BackLink
;
2733 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2734 if (SavedMenuOption
->Row
== 0) {
2735 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2737 Index
+= SavedMenuOption
->Skip
;
2741 // Found the menu which will show at the top of the page.
2743 if (Link
== NewPos
) {
2745 // The menu can show more than one pages, just show the menu at the top of the page.
2749 OldSkipValue
= SkipValue
;
2752 // Check whether need to skip some line for menu shows at the top of the page.
2754 SkipValue
= Index
- BottomRow
- 1;
2755 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2757 OldSkipValue
= SkipValue
;
2760 TopOfScreen
= Link
->ForwardLink
;
2766 ControlFlag
= CfRepaint
;
2771 // Target Question not found, highlight the default menu option
2773 NewPos
= TopOfScreen
;
2776 Selection
->QuestionId
= 0;
2779 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2780 if (MenuOption
!= NULL
) {
2782 // Remove highlight on last Menu Option
2784 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2785 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2786 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2787 if (OptionString
!= NULL
) {
2788 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2789 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2791 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2794 Width
= (UINT16
) gOptionBlockWidth
;
2795 OriginalRow
= MenuOption
->Row
;
2798 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2799 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2800 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2803 // If there is more string to process print on the next row and increment the Skip value
2805 if (StrLen (&OptionString
[Index
]) != 0) {
2809 FreePool (OutputString
);
2812 MenuOption
->Row
= OriginalRow
;
2814 FreePool (OptionString
);
2817 if (MenuOption
->GrayOut
) {
2818 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2819 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2820 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2823 OriginalRow
= MenuOption
->Row
;
2824 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2827 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2828 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2829 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2832 // If there is more string to process print on the next row and increment the Skip value
2834 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2838 FreePool (OutputString
);
2841 MenuOption
->Row
= OriginalRow
;
2842 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2848 // This is the current selected statement
2850 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2851 Statement
= MenuOption
->ThisTag
;
2852 Selection
->Statement
= Statement
;
2853 if (!IsSelectable (MenuOption
)) {
2854 Repaint
= SavedValue
;
2855 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2860 // Record highlight for current menu
2862 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2863 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2866 // Set reverse attribute
2868 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2869 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2872 // Assuming that we have a refresh linked-list created, lets annotate the
2873 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2874 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2876 if (gMenuRefreshHead
!= NULL
) {
2877 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2878 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2879 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2881 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2883 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2884 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2889 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2890 if (OptionString
!= NULL
) {
2891 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2892 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2894 Width
= (UINT16
) gOptionBlockWidth
;
2896 OriginalRow
= MenuOption
->Row
;
2899 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2900 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2901 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2904 // If there is more string to process print on the next row and increment the Skip value
2906 if (StrLen (&OptionString
[Index
]) != 0) {
2910 FreePool (OutputString
);
2913 MenuOption
->Row
= OriginalRow
;
2915 FreePool (OptionString
);
2918 OriginalRow
= MenuOption
->Row
;
2920 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2923 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2924 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2925 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2928 // If there is more string to process print on the next row and increment the Skip value
2930 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2934 FreePool (OutputString
);
2937 MenuOption
->Row
= OriginalRow
;
2942 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2945 // Clear reverse attribute
2947 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2950 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2951 // if we didn't break halfway when process CfRefreshHighLight.
2953 Repaint
= SavedValue
;
2956 case CfUpdateHelpString
:
2957 ControlFlag
= CfPrepareToReadKey
;
2958 if (Selection
->Form
->ModalForm
) {
2962 if (Repaint
|| NewLine
) {
2964 // Don't print anything if it is a NULL help token
2966 ASSERT(MenuOption
!= NULL
);
2967 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
2970 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2973 RowCount
= BottomRow
- TopRow
;
2976 // 1.Calculate how many line the help string need to print.
2978 if (HelpString
!= NULL
) {
2979 FreePool (HelpString
);
2981 HelpLine
= ProcessHelpString (StringPtr
, &HelpString
, &EachLineWidth
, RowCount
);
2982 if (HelpLine
> RowCount
) {
2983 MultiHelpPage
= TRUE
;
2984 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP
), gHiiHandle
);
2985 if (HelpHeaderString
!= NULL
) {
2986 FreePool (HelpHeaderString
);
2988 HelpHeaderLine
= ProcessHelpString (StringPtr
, &HelpHeaderString
, &HeaderLineWidth
, RowCount
);
2989 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN
), gHiiHandle
);
2990 if (HelpBottomString
!= NULL
) {
2991 FreePool (HelpBottomString
);
2993 HelpBottomLine
= ProcessHelpString (StringPtr
, &HelpBottomString
, &BottomLineWidth
, RowCount
);
2995 // Calculate the help page count.
2997 if (HelpLine
> 2 * RowCount
- 2) {
2998 HelpPageCount
= (HelpLine
- RowCount
+ 1) / (RowCount
- 2) + 1;
2999 if ((HelpLine
- RowCount
+ 1) % (RowCount
- 2) > 1) {
3006 MultiHelpPage
= FALSE
;
3011 // Clean the help field first.
3014 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3015 LocalScreen
.RightColumn
,
3018 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
3022 // Check whether need to show the 'More(U/u)' at the begin.
3023 // Base on current direct info, here shows aligned to the right side of the column.
3024 // If the direction is multi line and aligned to right side may have problem, so
3025 // add ASSERT code here.
3027 if (HelpPageIndex
> 0) {
3028 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3029 for (Index
= 0; Index
< HelpHeaderLine
; Index
++) {
3030 ASSERT (HelpHeaderLine
== 1);
3031 ASSERT (GetStringWidth (HelpHeaderString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3033 LocalScreen
.RightColumn
- GetStringWidth (HelpHeaderString
) / 2 - 1,
3035 &HelpHeaderString
[Index
* HeaderLineWidth
]
3040 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
3042 // Print the help string info.
3044 if (!MultiHelpPage
) {
3045 for (Index
= 0; Index
< HelpLine
; Index
++) {
3047 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3049 &HelpString
[Index
* EachLineWidth
]
3052 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3054 if (HelpPageIndex
== 0) {
3055 for (Index
= 0; Index
< RowCount
- HelpBottomLine
; Index
++) {
3057 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3059 &HelpString
[Index
* EachLineWidth
]
3063 for (Index
= 0; (Index
< RowCount
- HelpBottomLine
- HelpHeaderLine
) &&
3064 (Index
+ HelpPageIndex
* (RowCount
- 2) + 1 < HelpLine
); Index
++) {
3066 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3067 Index
+ TopRow
+ HelpHeaderLine
,
3068 &HelpString
[(Index
+ HelpPageIndex
* (RowCount
- 2) + 1)* EachLineWidth
]
3071 if (HelpPageIndex
== HelpPageCount
- 1) {
3072 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3078 // Check whether need to print the 'More(D/d)' at the bottom.
3079 // Base on current direct info, here shows aligned to the right side of the column.
3080 // If the direction is multi line and aligned to right side may have problem, so
3081 // add ASSERT code here.
3083 if (HelpPageIndex
< HelpPageCount
- 1 && MultiHelpPage
) {
3084 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3085 for (Index
= 0; Index
< HelpBottomLine
; Index
++) {
3086 ASSERT (HelpBottomLine
== 1);
3087 ASSERT (GetStringWidth (HelpBottomString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3089 LocalScreen
.RightColumn
- GetStringWidth (HelpBottomString
) / 2 - 1,
3090 Index
+ BottomRow
- HelpBottomLine
,
3091 &HelpBottomString
[Index
* BottomLineWidth
]
3096 // Reset this flag every time we finish using it.
3102 case CfPrepareToReadKey
:
3103 ControlFlag
= CfReadKey
;
3104 ScreenOperation
= UiNoOperation
;
3108 ControlFlag
= CfScreenOperation
;
3111 // Wait for user's selection
3114 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
3115 } while (Status
== EFI_TIMEOUT
);
3117 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
3119 // IFR is updated in Callback of refresh opcode, re-parse it
3121 ControlFlag
= CfCheckSelection
;
3122 Selection
->Statement
= NULL
;
3126 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
3128 // If we encounter error, continue to read another key in.
3130 if (EFI_ERROR (Status
)) {
3131 ControlFlag
= CfReadKey
;
3135 switch (Key
.UnicodeChar
) {
3136 case CHAR_CARRIAGE_RETURN
:
3137 if(MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3138 ControlFlag
= CfReadKey
;
3142 ScreenOperation
= UiSelect
;
3147 // We will push the adjustment of these numeric values directly to the input handler
3148 // NOTE: we won't handle manual input numeric
3153 // If the screen has no menu items, and the user didn't select UiReset
3154 // ignore the selection and go back to reading keys.
3156 if(IsListEmpty (&gMenuOption
) || MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3157 ControlFlag
= CfReadKey
;
3161 ASSERT(MenuOption
!= NULL
);
3162 Statement
= MenuOption
->ThisTag
;
3163 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
3164 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
3165 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
3167 if (Key
.UnicodeChar
== '+') {
3168 gDirection
= SCAN_RIGHT
;
3170 gDirection
= SCAN_LEFT
;
3172 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3173 if (EFI_ERROR (Status
)) {
3175 // Repaint to clear possible error prompt pop-up
3180 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3182 if (OptionString
!= NULL
) {
3183 FreePool (OptionString
);
3189 ScreenOperation
= UiUp
;
3194 ScreenOperation
= UiDown
;
3198 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
3200 // If the screen has no menu items, and the user didn't select UiReset
3201 // ignore the selection and go back to reading keys.
3203 if(IsListEmpty (&gMenuOption
)) {
3204 ControlFlag
= CfReadKey
;
3208 ASSERT(MenuOption
!= NULL
);
3209 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
&& !MenuOption
->ReadOnly
) {
3210 ScreenOperation
= UiSelect
;
3217 if (!MultiHelpPage
) {
3218 ControlFlag
= CfReadKey
;
3221 ControlFlag
= CfUpdateHelpString
;
3222 HelpPageIndex
= HelpPageIndex
< HelpPageCount
- 1 ? HelpPageIndex
+ 1 : HelpPageCount
- 1;
3227 if (!MultiHelpPage
) {
3228 ControlFlag
= CfReadKey
;
3231 ControlFlag
= CfUpdateHelpString
;
3232 HelpPageIndex
= HelpPageIndex
> 0 ? HelpPageIndex
- 1 : 0;
3236 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
3237 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
3238 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
3243 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
3245 // ModalForm has no ESC key and Hot Key.
3247 ControlFlag
= CfReadKey
;
3248 } else if (Index
== mScanCodeNumber
) {
3250 // Check whether Key matches the registered hot key.
3253 if ((gBrowserSettingScope
== SystemLevel
) || (gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
3254 HotKey
= GetHotKeyFromRegisterList (&Key
);
3256 if (HotKey
!= NULL
) {
3257 ScreenOperation
= UiHotKey
;
3264 case CfScreenOperation
:
3265 if (ScreenOperation
!= UiReset
) {
3267 // If the screen has no menu items, and the user didn't select UiReset
3268 // ignore the selection and go back to reading keys.
3270 if (IsListEmpty (&gMenuOption
)) {
3271 ControlFlag
= CfReadKey
;
3277 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
3280 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
3281 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
3288 ControlFlag
= CfCheckSelection
;
3290 ASSERT(MenuOption
!= NULL
);
3291 Statement
= MenuOption
->ThisTag
;
3292 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
3297 // Keep highlight on current MenuOption
3299 Selection
->QuestionId
= Statement
->QuestionId
;
3301 switch (Statement
->Operand
) {
3302 case EFI_IFR_REF_OP
:
3303 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
3306 case EFI_IFR_ACTION_OP
:
3308 // Process the Config string <ConfigResp>
3310 Status
= ProcessQuestionConfig (Selection
, Statement
);
3312 if (EFI_ERROR (Status
)) {
3317 // The action button may change some Question value, so refresh the form
3319 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3322 case EFI_IFR_RESET_BUTTON_OP
:
3324 // Reset Question to default value specified by DefaultId
3326 ControlFlag
= CfUiDefault
;
3327 DefaultId
= Statement
->DefaultId
;
3332 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3334 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
3335 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3337 if (EFI_ERROR (Status
)) {
3340 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
3342 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3345 if (OptionString
!= NULL
) {
3346 FreePool (OptionString
);
3354 // We come here when someone press ESC
3356 ControlFlag
= CfCheckSelection
;
3357 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3361 ControlFlag
= CfCheckSelection
;
3362 ASSERT(MenuOption
!= NULL
);
3363 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3364 if (MenuOption
->Sequence
!= 0) {
3366 // In the middle or tail of the Date/Time op-code set, go left.
3368 ASSERT(NewPos
!= NULL
);
3369 NewPos
= NewPos
->BackLink
;
3375 ControlFlag
= CfCheckSelection
;
3376 ASSERT(MenuOption
!= NULL
);
3377 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3378 if (MenuOption
->Sequence
!= 2) {
3380 // In the middle or tail of the Date/Time op-code set, go left.
3382 ASSERT(NewPos
!= NULL
);
3383 NewPos
= NewPos
->ForwardLink
;
3389 ControlFlag
= CfCheckSelection
;
3391 SavedListEntry
= NewPos
;
3393 ASSERT(NewPos
!= NULL
);
3395 // Adjust Date/Time position before we advance forward.
3397 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3398 if (NewPos
->BackLink
!= &gMenuOption
) {
3399 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3400 ASSERT (MenuOption
!= NULL
);
3402 NewPos
= NewPos
->BackLink
;
3404 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3405 if (PreviousMenuOption
->Row
== 0) {
3406 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3408 DistanceValue
= PreviousMenuOption
->Skip
;
3410 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3411 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3413 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3415 if (Difference
< 0) {
3417 // We hit the begining MenuOption that can be focused
3418 // so we simply scroll to the top.
3420 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3421 TopOfScreen
= gMenuOption
.ForwardLink
;
3425 // Scroll up to the last page when we have arrived at top page.
3427 NewPos
= &gMenuOption
;
3428 TopOfScreen
= &gMenuOption
;
3429 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3430 ScreenOperation
= UiPageUp
;
3431 ControlFlag
= CfScreenOperation
;
3434 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3436 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3438 TopOfScreen
= NewPos
;
3442 } else if (!IsSelectable (NextMenuOption
)) {
3444 // Continue to go up until scroll to next page or the selectable option is found.
3446 ScreenOperation
= UiUp
;
3447 ControlFlag
= CfScreenOperation
;
3451 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3453 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3454 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3455 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3456 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3459 // Scroll up to the last page.
3461 NewPos
= &gMenuOption
;
3462 TopOfScreen
= &gMenuOption
;
3463 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3464 ScreenOperation
= UiPageUp
;
3465 ControlFlag
= CfScreenOperation
;
3470 ControlFlag
= CfCheckSelection
;
3472 ASSERT(NewPos
!= NULL
);
3473 if (NewPos
->BackLink
== &gMenuOption
) {
3483 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3484 Link
= Link
->BackLink
;
3485 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3486 if (PreviousMenuOption
->Row
== 0) {
3487 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3489 if (Index
< PreviousMenuOption
->Skip
) {
3493 Index
= Index
- PreviousMenuOption
->Skip
;
3496 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3497 if (TopOfScreen
== &gMenuOption
) {
3498 TopOfScreen
= gMenuOption
.ForwardLink
;
3499 NewPos
= gMenuOption
.BackLink
;
3500 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3502 } else if (TopOfScreen
!= Link
) {
3505 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3508 // Finally we know that NewPos is the last MenuOption can be focused.
3512 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3515 if (Index
+ 1 < TopRow
) {
3517 // Back up the previous option.
3519 Link
= Link
->ForwardLink
;
3523 // Move to the option in Next page.
3525 if (TopOfScreen
== &gMenuOption
) {
3526 NewPos
= gMenuOption
.BackLink
;
3527 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3530 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3534 // There are more MenuOption needing scrolling up.
3541 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3542 // Don't do this when we are already in the first page.
3544 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3545 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3549 ControlFlag
= CfCheckSelection
;
3551 ASSERT (NewPos
!= NULL
);
3552 if (NewPos
->ForwardLink
== &gMenuOption
) {
3561 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3563 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
3564 Index
= Index
+ NextMenuOption
->Skip
;
3565 Link
= Link
->ForwardLink
;
3566 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3569 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
)) {
3571 // Finally we know that NewPos is the last MenuOption can be focused.
3574 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3576 if (Index
- 1 > BottomRow
) {
3578 // Back up the previous option.
3580 Link
= Link
->BackLink
;
3583 // There are more MenuOption needing scrolling down.
3588 // Move to the option in Next page.
3590 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3594 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3595 // Don't do this when we are already in the last page.
3598 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3599 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3603 ControlFlag
= CfCheckSelection
;
3605 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3606 // to be one that progresses to the next set of op-codes, we need to advance to the last
3607 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3608 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3609 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3610 // the Date/Time op-code.
3612 SavedListEntry
= NewPos
;
3613 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3615 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3616 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3618 NewPos
= NewPos
->ForwardLink
;
3621 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3622 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3624 // We hit the end of MenuOption that can be focused
3625 // so we simply scroll to the first page.
3627 if (Difference
< 0) {
3629 // Scroll to the first page.
3631 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3632 TopOfScreen
= gMenuOption
.ForwardLink
;
3636 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3638 NewPos
= gMenuOption
.ForwardLink
;
3639 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3642 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3644 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3645 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3649 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3652 // An option might be multi-line, so we need to reflect that data in the overall skip value
3654 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3655 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3657 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3658 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3659 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3660 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3666 // If we are going to scroll, update TopOfScreen
3668 if (Temp
> BottomRow
) {
3671 // Is the current top of screen a zero-advance op-code?
3672 // If so, keep moving forward till we hit a >0 advance op-code
3674 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3677 // If bottom op-code is more than one line or top op-code is more than one line
3679 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3681 // Is the bottom op-code greater than or equal in size to the top op-code?
3683 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3685 // Skip the top op-code
3687 TopOfScreen
= TopOfScreen
->ForwardLink
;
3688 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3690 OldSkipValue
= Difference
;
3692 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3695 // If we have a remainder, skip that many more op-codes until we drain the remainder
3697 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3699 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3701 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3702 TopOfScreen
= TopOfScreen
->ForwardLink
;
3703 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3706 // Since we will act on this op-code in the next routine, and increment the
3707 // SkipValue, set the skips to one less than what is required.
3709 SkipValue
= Difference
- 1;
3713 // Since we will act on this op-code in the next routine, and increment the
3714 // SkipValue, set the skips to one less than what is required.
3716 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3719 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3720 TopOfScreen
= TopOfScreen
->ForwardLink
;
3723 SkipValue
= OldSkipValue
;
3727 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3728 // Let's set a skip flag to smoothly scroll the top of the screen.
3730 if (SavedMenuOption
->Skip
> 1) {
3731 if (SavedMenuOption
== NextMenuOption
) {
3736 } else if (SavedMenuOption
->Skip
== 1) {
3740 TopOfScreen
= TopOfScreen
->ForwardLink
;
3742 } while (SavedMenuOption
->Skip
== 0);
3745 OldSkipValue
= SkipValue
;
3746 } else if (!IsSelectable (NextMenuOption
)) {
3748 // Continue to go down until scroll to next page or the selectable option is found.
3750 ScreenOperation
= UiDown
;
3751 ControlFlag
= CfScreenOperation
;
3754 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3756 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3760 // Scroll to the first page.
3762 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3763 TopOfScreen
= gMenuOption
.ForwardLink
;
3767 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3770 NewPos
= gMenuOption
.ForwardLink
;
3771 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3775 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3777 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3778 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3782 ControlFlag
= CfCheckSelection
;
3784 Status
= EFI_SUCCESS
;
3786 // Discard changes. After it, no NV flag is showed.
3788 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3789 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3790 if (!EFI_ERROR (Status
)) {
3791 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3792 Selection
->Statement
= NULL
;
3793 gResetRequired
= FALSE
;
3796 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3797 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3799 // Still show current page.
3801 Selection
->Action
= UI_ACTION_NONE
;
3809 // Reterieve default setting. After it. NV flag will be showed.
3811 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3812 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
, GetDefaultForAll
, NULL
);
3813 if (!EFI_ERROR (Status
)) {
3814 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3815 Selection
->Statement
= NULL
;
3816 gResetRequired
= TRUE
;
3819 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3820 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3822 // Still show current page.
3824 Selection
->Action
= UI_ACTION_NONE
;
3832 // Save changes. After it, no NV flag is showed.
3834 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3835 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3836 if (!EFI_ERROR (Status
)) {
3837 ASSERT(MenuOption
!= NULL
);
3838 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3839 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3842 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3843 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3845 // Still show current page.
3847 Selection
->Action
= UI_ACTION_NONE
;
3855 // Set Reset required Flag
3857 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3858 gResetRequired
= TRUE
;
3864 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3866 // Form Exit without saving, Similar to ESC Key.
3867 // FormSet Exit without saving, Exit SendForm.
3868 // System Exit without saving, CallExitHandler and Exit SendForm.
3870 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3871 if (gBrowserSettingScope
== FormLevel
) {
3872 ControlFlag
= CfUiReset
;
3873 } else if (gBrowserSettingScope
== FormSetLevel
) {
3874 Selection
->Action
= UI_ACTION_EXIT
;
3875 } else if (gBrowserSettingScope
== SystemLevel
) {
3876 if (ExitHandlerFunction
!= NULL
) {
3877 ExitHandlerFunction ();
3879 Selection
->Action
= UI_ACTION_EXIT
;
3881 Selection
->Statement
= NULL
;
3886 ControlFlag
= CfCheckSelection
;
3888 // Reset to default value for all forms in the whole system.
3890 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
, GetDefaultForAll
, NULL
);
3892 if (!EFI_ERROR (Status
)) {
3893 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3894 Selection
->Statement
= NULL
;
3895 gResetRequired
= TRUE
;
3899 case CfUiNoOperation
:
3900 ControlFlag
= CfCheckSelection
;
3904 UiFreeRefreshList ();
3906 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3907 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3908 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3909 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");
3910 if (HelpString
!= NULL
) {
3911 FreePool (HelpString
);
3913 if (HelpHeaderString
!= NULL
) {
3914 FreePool (HelpHeaderString
);
3916 if (HelpBottomString
!= NULL
) {
3917 FreePool (HelpBottomString
);