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
;
772 MenuOption
->IsQuestion
= FALSE
;
776 if ((Statement
->ValueExpression
!= NULL
) ||
777 ((Statement
->QuestionFlags
& EFI_IFR_FLAG_READ_ONLY
) != 0)) {
778 MenuOption
->ReadOnly
= TRUE
;
781 InsertTailList (&gMenuOption
, &MenuOption
->Link
);
789 Routine used to abstract a generic dialog interface and return the selected key or string
791 @param NumberOfLines The number of lines for the dialog box
792 @param HotKey Defines whether a single character is parsed
793 (TRUE) and returned in KeyValue or a string is
794 returned in StringBuffer. Two special characters
795 are considered when entering a string, a SCAN_ESC
796 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
797 string input and returns
798 @param MaximumStringSize The maximum size in bytes of a typed in string
799 (each character is a CHAR16) and the minimum
800 string returned is two bytes
801 @param StringBuffer The passed in pointer to the buffer which will
802 hold the typed in string if HotKey is FALSE
803 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
804 @param ... A series of (quantity == NumberOfLines) text
805 strings which will be used to construct the dialog
808 @retval EFI_SUCCESS Displayed dialog and received user interaction
809 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
810 (StringBuffer == NULL) && (HotKey == FALSE))
811 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
817 IN UINTN NumberOfLines
,
819 IN UINTN MaximumStringSize
,
820 OUT CHAR16
*StringBuffer
,
821 OUT EFI_INPUT_KEY
*KeyValue
,
830 CHAR16
*BufferedString
;
837 BOOLEAN SelectionComplete
;
839 UINTN CurrentAttribute
;
840 UINTN DimensionsWidth
;
841 UINTN DimensionsHeight
;
843 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
844 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
846 SelectionComplete
= FALSE
;
848 TempString
= AllocateZeroPool (MaximumStringSize
* 2);
849 BufferedString
= AllocateZeroPool (MaximumStringSize
* 2);
850 CurrentAttribute
= gST
->ConOut
->Mode
->Attribute
;
853 ASSERT (BufferedString
);
856 // Zero the outgoing buffer
858 ZeroMem (StringBuffer
, MaximumStringSize
);
861 if (KeyValue
== NULL
) {
862 return EFI_INVALID_PARAMETER
;
865 if (StringBuffer
== NULL
) {
866 return EFI_INVALID_PARAMETER
;
872 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
876 VA_START (Marker
, KeyValue
);
879 // Determine the largest string in the dialog box
880 // Notice we are starting with 1 since String is the first string
882 for (Count
= 0; Count
< NumberOfLines
; Count
++) {
883 StackString
= VA_ARG (Marker
, CHAR16
*);
885 if (StackString
[0] == L
' ') {
886 InputOffset
= Count
+ 1;
889 if ((GetStringWidth (StackString
) / 2) > LargestString
) {
891 // Size of the string visually and subtract the width by one for the null-terminator
893 LargestString
= (GetStringWidth (StackString
) / 2);
898 Start
= (DimensionsWidth
- LargestString
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
899 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
906 VA_START (Marker
, KeyValue
);
907 CreateSharedPopUp (LargestString
, NumberOfLines
, Marker
);
911 // Take the first key typed and report it back?
914 Status
= WaitForKeyStroke (&Key
);
915 ASSERT_EFI_ERROR (Status
);
916 CopyMem (KeyValue
, &Key
, sizeof (EFI_INPUT_KEY
));
920 Status
= WaitForKeyStroke (&Key
);
922 switch (Key
.UnicodeChar
) {
924 switch (Key
.ScanCode
) {
926 FreePool (TempString
);
927 FreePool (BufferedString
);
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
929 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
930 return EFI_DEVICE_ERROR
;
938 case CHAR_CARRIAGE_RETURN
:
939 SelectionComplete
= TRUE
;
940 FreePool (TempString
);
941 FreePool (BufferedString
);
942 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
943 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
948 if (StringBuffer
[0] != CHAR_NULL
) {
949 for (Index
= 0; StringBuffer
[Index
] != CHAR_NULL
; Index
++) {
950 TempString
[Index
] = StringBuffer
[Index
];
953 // Effectively truncate string by 1 character
955 TempString
[Index
- 1] = CHAR_NULL
;
956 StrCpy (StringBuffer
, TempString
);
961 // If it is the beginning of the string, don't worry about checking maximum limits
963 if ((StringBuffer
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
964 StrnCpy (StringBuffer
, &Key
.UnicodeChar
, 1);
965 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
966 } else if ((GetStringWidth (StringBuffer
) < MaximumStringSize
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
967 KeyPad
[0] = Key
.UnicodeChar
;
968 KeyPad
[1] = CHAR_NULL
;
969 StrCat (StringBuffer
, KeyPad
);
970 StrCat (TempString
, KeyPad
);
973 // If the width of the input string is now larger than the screen, we nee to
974 // adjust the index to start printing portions of the string
976 SetUnicodeMem (BufferedString
, LargestString
, L
' ');
978 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
980 if ((GetStringWidth (StringBuffer
) / 2) > (DimensionsWidth
- 2)) {
981 Index
= (GetStringWidth (StringBuffer
) / 2) - DimensionsWidth
+ 2;
986 for (Count
= 0; Index
+ 1 < GetStringWidth (StringBuffer
) / 2; Index
++, Count
++) {
987 BufferedString
[Count
] = StringBuffer
[Index
];
990 PrintStringAt (Start
+ 1, Top
+ InputOffset
, BufferedString
);
993 } while (!SelectionComplete
);
996 gST
->ConOut
->SetAttribute (gST
->ConOut
, CurrentAttribute
);
997 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
1002 Draw a pop up windows based on the dimension, number of lines and
1005 @param RequestedWidth The width of the pop-up.
1006 @param NumberOfLines The number of lines.
1007 @param Marker The variable argument list for the list of string to be printed.
1012 IN UINTN RequestedWidth
,
1013 IN UINTN NumberOfLines
,
1025 UINTN DimensionsWidth
;
1026 UINTN DimensionsHeight
;
1028 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
1029 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
1031 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1033 if ((RequestedWidth
+ 2) > DimensionsWidth
) {
1034 RequestedWidth
= DimensionsWidth
- 2;
1038 // Subtract the PopUp width from total Columns, allow for one space extra on
1039 // each end plus a border.
1041 Start
= (DimensionsWidth
- RequestedWidth
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
1042 End
= Start
+ RequestedWidth
+ 1;
1044 Top
= ((DimensionsHeight
- NumberOfLines
- 2) / 2) + gScreenDimensions
.TopRow
- 1;
1045 Bottom
= Top
+ NumberOfLines
+ 2;
1047 Character
= BOXDRAW_DOWN_RIGHT
;
1048 PrintCharAt (Start
, Top
, Character
);
1049 Character
= BOXDRAW_HORIZONTAL
;
1050 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1051 PrintChar (Character
);
1054 Character
= BOXDRAW_DOWN_LEFT
;
1055 PrintChar (Character
);
1056 Character
= BOXDRAW_VERTICAL
;
1059 for (Index
= Top
; Index
+ 2 < Bottom
; Index
++, Count
++) {
1060 String
= VA_ARG (Marker
, CHAR16
*);
1063 // This will clear the background of the line - we never know who might have been
1064 // here before us. This differs from the next clear in that it used the non-reverse
1065 // video for normal printing.
1067 if (GetStringWidth (String
) / 2 > 1) {
1068 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1072 // Passing in a space results in the assumption that this is where typing will occur
1074 if (String
[0] == L
' ') {
1075 ClearLines (Start
+ 1, End
- 1, Index
+ 1, Index
+ 1, POPUP_INVERSE_TEXT
| POPUP_INVERSE_BACKGROUND
);
1079 // Passing in a NULL results in a blank space
1081 if (String
[0] == CHAR_NULL
) {
1082 ClearLines (Start
, End
, Index
+ 1, Index
+ 1, POPUP_TEXT
| POPUP_BACKGROUND
);
1086 ((DimensionsWidth
- GetStringWidth (String
) / 2) / 2) + gScreenDimensions
.LeftColumn
+ 1,
1090 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1091 PrintCharAt (Start
, Index
+ 1, Character
);
1092 PrintCharAt (End
- 1, Index
+ 1, Character
);
1095 Character
= BOXDRAW_UP_RIGHT
;
1096 PrintCharAt (Start
, Bottom
- 1, Character
);
1097 Character
= BOXDRAW_HORIZONTAL
;
1098 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1099 PrintChar (Character
);
1102 Character
= BOXDRAW_UP_LEFT
;
1103 PrintChar (Character
);
1107 Draw a pop up windows based on the dimension, number of lines and
1110 @param RequestedWidth The width of the pop-up.
1111 @param NumberOfLines The number of lines.
1112 @param ... A series of text strings that displayed in the pop-up.
1117 CreateMultiStringPopUp (
1118 IN UINTN RequestedWidth
,
1119 IN UINTN NumberOfLines
,
1125 VA_START (Marker
, NumberOfLines
);
1127 CreateSharedPopUp (RequestedWidth
, NumberOfLines
, Marker
);
1134 Update status bar on the bottom of menu.
1136 @param Selection Current Selction info.
1137 @param MessageType The type of message to be shown.
1138 @param Flags The flags in Question header.
1139 @param State Set or clear.
1144 IN UI_MENU_SELECTION
*Selection
,
1145 IN UINTN MessageType
,
1151 CHAR16
*NvUpdateMessage
;
1152 CHAR16
*InputErrorMessage
;
1154 FORM_BROWSER_FORMSET
*LocalFormSet
;
1155 FORM_BROWSER_STATEMENT
*Question
;
1157 NvUpdateMessage
= GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE
), gHiiHandle
);
1158 InputErrorMessage
= GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE
), gHiiHandle
);
1160 switch (MessageType
) {
1163 gST
->ConOut
->SetAttribute (gST
->ConOut
, ERROR_TEXT
);
1165 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
,
1166 gScreenDimensions
.BottomRow
- 1,
1171 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1172 for (Index
= 0; Index
< (GetStringWidth (InputErrorMessage
) - 2) / 2; Index
++) {
1173 PrintAt (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ Index
, gScreenDimensions
.BottomRow
- 1, L
" ");
1176 mInputError
= FALSE
;
1180 case NV_UPDATE_REQUIRED
:
1182 // Global setting support. Show configuration change on every form.
1185 gResetRequired
= (BOOLEAN
) (gResetRequired
| ((Flags
& EFI_IFR_FLAG_RESET_REQUIRED
) == EFI_IFR_FLAG_RESET_REQUIRED
));
1187 if (Selection
!= NULL
&& Selection
->Statement
!= NULL
) {
1188 Question
= Selection
->Statement
;
1189 if (Question
->Storage
!= NULL
|| Question
->Operand
== EFI_IFR_DATE_OP
|| Question
->Operand
== EFI_IFR_TIME_OP
) {
1191 // Update only for Question value that need to be saved into Storage.
1193 Selection
->Form
->NvUpdateRequired
= TRUE
;
1197 if (Selection
== NULL
|| IsNvUpdateRequired (Selection
->FormSet
)) {
1198 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
);
1200 gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
1201 gScreenDimensions
.BottomRow
- 1,
1206 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
));
1207 for (Index
= 0; Index
< (GetStringWidth (NvUpdateMessage
) - 2) / 2; Index
++) {
1209 (gScreenDimensions
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ Index
),
1210 gScreenDimensions
.BottomRow
- 1,
1217 case REFRESH_STATUS_BAR
:
1219 UpdateStatusBar (Selection
, INPUT_ERROR
, Flags
, TRUE
);
1222 switch (gBrowserSettingScope
) {
1225 // Check the maintain list to see whether there is any change.
1227 Link
= GetFirstNode (&gBrowserFormSetList
);
1228 while (!IsNull (&gBrowserFormSetList
, Link
)) {
1229 LocalFormSet
= FORM_BROWSER_FORMSET_FROM_LINK (Link
);
1230 if (IsNvUpdateRequired(LocalFormSet
)) {
1231 UpdateStatusBar (NULL
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1234 Link
= GetNextNode (&gBrowserFormSetList
, Link
);
1239 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Flags
, TRUE
);
1250 FreePool (InputErrorMessage
);
1251 FreePool (NvUpdateMessage
);
1257 Get the supported width for a particular op-code
1259 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1260 @param Handle The handle in the HII database being used
1262 @return Returns the number of CHAR16 characters that is support.
1267 IN FORM_BROWSER_STATEMENT
*Statement
,
1268 IN EFI_HII_HANDLE Handle
1278 // See if the second text parameter is really NULL
1280 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
1281 String
= GetToken (Statement
->TextTwo
, Handle
);
1282 Size
= StrLen (String
);
1286 if ((Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1287 (Statement
->Operand
== EFI_IFR_REF_OP
) ||
1288 (Statement
->Operand
== EFI_IFR_PASSWORD_OP
) ||
1289 (Statement
->Operand
== EFI_IFR_ACTION_OP
) ||
1290 (Statement
->Operand
== EFI_IFR_RESET_BUTTON_OP
) ||
1292 // Allow a wide display if text op-code and no secondary text op-code
1294 ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Size
== 0))
1296 Width
= (UINT16
) (gPromptBlockWidth
+ gOptionBlockWidth
);
1298 Width
= (UINT16
) gPromptBlockWidth
;
1301 if (Statement
->InSubtitle
) {
1302 Width
-= SUBTITLE_INDENT
;
1305 return (UINT16
) (Width
- LEFT_SKIPPED_COLUMNS
);
1309 Will copy LineWidth amount of a string in the OutputString buffer and return the
1310 number of CHAR16 characters that were copied into the OutputString buffer.
1311 The output string format is:
1312 Glyph Info + String info + '\0'.
1314 In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
1316 @param InputString String description for this option.
1317 @param LineWidth Width of the desired string to extract in CHAR16
1319 @param GlyphWidth The glyph width of the begin of the char in the string.
1320 @param Index Where in InputString to start the copy process
1321 @param OutputString Buffer to copy the string into
1323 @return Returns the number of CHAR16 characters that were copied into the OutputString
1324 buffer, include extra glyph info and '\0' info.
1329 IN CHAR16
*InputString
,
1330 IN UINT16 LineWidth
,
1331 IN OUT UINT16
*GlyphWidth
,
1332 IN OUT UINTN
*Index
,
1333 OUT CHAR16
**OutputString
1338 UINT16 OriginalGlyphWidth
;
1340 UINT16 LastSpaceOffset
;
1341 UINT16 LastGlyphWidth
;
1343 if (InputString
== NULL
|| Index
== NULL
|| OutputString
== NULL
) {
1347 if (LineWidth
== 0 || *GlyphWidth
== 0) {
1352 // Save original glyph width.
1354 OriginalGlyphWidth
= *GlyphWidth
;
1355 LastGlyphWidth
= OriginalGlyphWidth
;
1357 LastSpaceOffset
= 0;
1360 // 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.
1361 // To avoid displaying this empty line in screen, just skip the two CHARs here.
1363 if ((InputString
[*Index
] == NARROW_CHAR
) && (InputString
[*Index
+ 1] == CHAR_CARRIAGE_RETURN
)) {
1364 *Index
= *Index
+ 2;
1368 // Fast-forward the string and see if there is a carriage-return in the string
1370 for (StrOffset
= 0, GlyphOffset
= 0; GlyphOffset
<= LineWidth
; StrOffset
++) {
1371 switch (InputString
[*Index
+ StrOffset
]) {
1380 case CHAR_CARRIAGE_RETURN
:
1387 GlyphOffset
= GlyphOffset
+ *GlyphWidth
;
1390 // Record the last space info in this line. Will be used in rewind.
1392 if ((InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) && (GlyphOffset
<= LineWidth
)) {
1393 LastSpaceOffset
= StrOffset
;
1394 LastGlyphWidth
= *GlyphWidth
;
1405 // Rewind the string from the maximum size until we see a space to break the line
1407 if (GlyphOffset
> LineWidth
) {
1409 // Rewind the string to last space char in this line.
1411 if (LastSpaceOffset
!= 0) {
1412 StrOffset
= LastSpaceOffset
;
1413 *GlyphWidth
= LastGlyphWidth
;
1416 // Roll back to last char in the line width.
1423 // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
1425 if (StrOffset
== 0 && (InputString
[*Index
+ StrOffset
] == CHAR_NULL
)) {
1430 // Need extra glyph info and '\0' info, so +2.
1432 *OutputString
= AllocateZeroPool (((UINTN
) (StrOffset
+ 2) * sizeof(CHAR16
)));
1433 if (*OutputString
== NULL
) {
1438 // Save the glyph info at the begin of the string, will used by Print function.
1440 if (OriginalGlyphWidth
== 1) {
1441 *(*OutputString
) = NARROW_CHAR
;
1443 *(*OutputString
) = WIDE_CHAR
;
1446 CopyMem ((*OutputString
) + 1, &InputString
[*Index
], StrOffset
* sizeof(CHAR16
));
1448 if (InputString
[*Index
+ StrOffset
] == CHAR_SPACE
) {
1450 // Skip the space info at the begin of next line.
1452 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1453 } else if ((InputString
[*Index
+ StrOffset
] == CHAR_LINEFEED
)) {
1455 // Skip the /n or /n/r info.
1457 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_CARRIAGE_RETURN
) {
1458 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1460 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1462 } else if ((InputString
[*Index
+ StrOffset
] == CHAR_CARRIAGE_RETURN
)) {
1464 // Skip the /r or /r/n info.
1466 if (InputString
[*Index
+ StrOffset
+ 1] == CHAR_LINEFEED
) {
1467 *Index
= (UINT16
) (*Index
+ StrOffset
+ 2);
1469 *Index
= (UINT16
) (*Index
+ StrOffset
+ 1);
1472 *Index
= (UINT16
) (*Index
+ StrOffset
);
1476 // Include extra glyph info and '\0' info, so +2.
1478 return StrOffset
+ 2;
1483 Update display lines for a Menu Option.
1485 @param Selection The user's selection.
1486 @param MenuOption The MenuOption to be checked.
1490 UpdateOptionSkipLines (
1491 IN UI_MENU_SELECTION
*Selection
,
1492 IN UI_MENU_OPTION
*MenuOption
1499 CHAR16
*OutputString
;
1500 CHAR16
*OptionString
;
1504 OptionString
= NULL
;
1505 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
1507 if (OptionString
!= NULL
) {
1508 Width
= (UINT16
) gOptionBlockWidth
;
1513 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
1515 // If there is more string to process print on the next row and increment the Skip value
1517 if (StrLen (&OptionString
[Index
]) != 0) {
1520 // Since the Number of lines for this menu entry may or may not be reflected accurately
1521 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1522 // some testing to ensure we are keeping this in-sync.
1524 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1526 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
1531 FreePool (OutputString
);
1537 if (OptionString
!= NULL
) {
1538 FreePool (OptionString
);
1544 Check whether this Menu Option could be highlighted.
1546 This is an internal function.
1548 @param MenuOption The MenuOption to be checked.
1550 @retval TRUE This Menu Option is selectable.
1551 @retval FALSE This Menu Option could not be selected.
1556 UI_MENU_OPTION
*MenuOption
1559 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) ||
1560 MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
1569 Determine if the menu is the last menu that can be selected.
1571 This is an internal function.
1573 @param Direction The scroll direction. False is down. True is up.
1574 @param CurrentPos The current focus.
1576 @return FALSE -- the menu isn't the last menu that can be selected.
1577 @return TRUE -- the menu is the last menu that can be selected.
1582 IN BOOLEAN Direction
,
1583 IN LIST_ENTRY
*CurrentPos
1588 Temp
= Direction
? CurrentPos
->BackLink
: CurrentPos
->ForwardLink
;
1590 if (Temp
== &gMenuOption
) {
1599 Move to next selectable statement.
1601 This is an internal function.
1603 @param Selection Menu selection.
1604 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1605 @param CurrentPosition Current position.
1606 @param GapToTop Gap position to top or bottom.
1608 @return The row distance from current MenuOption to next selectable MenuOption.
1612 MoveToNextStatement (
1613 IN UI_MENU_SELECTION
*Selection
,
1615 IN OUT LIST_ENTRY
**CurrentPosition
,
1621 UI_MENU_OPTION
*NextMenuOption
;
1622 UI_MENU_OPTION
*PreMenuOption
;
1625 Pos
= *CurrentPosition
;
1626 PreMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1629 NextMenuOption
= MENU_OPTION_FROM_LINK (Pos
);
1630 if (NextMenuOption
->Row
== 0) {
1631 UpdateOptionSkipLines (Selection
, NextMenuOption
);
1634 if (GoUp
&& (PreMenuOption
!= NextMenuOption
)) {
1636 // Current Position doesn't need to be caculated when go up.
1637 // Caculate distanct at first when go up
1639 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1640 NextMenuOption
= PreMenuOption
;
1643 Distance
+= NextMenuOption
->Skip
;
1645 if (IsSelectable (NextMenuOption
)) {
1648 if ((GoUp
? Pos
->BackLink
: Pos
->ForwardLink
) == &gMenuOption
) {
1657 // Caculate distanct at later when go down
1659 if ((UINTN
) Distance
+ NextMenuOption
->Skip
> GapToTop
) {
1660 NextMenuOption
= PreMenuOption
;
1663 Distance
+= NextMenuOption
->Skip
;
1665 PreMenuOption
= NextMenuOption
;
1666 Pos
= (GoUp
? Pos
->BackLink
: Pos
->ForwardLink
);
1669 *CurrentPosition
= &NextMenuOption
->Link
;
1675 Adjust Data and Time position accordingly.
1676 Data format : [01/02/2004] [11:22:33]
1677 Line number : 0 0 1 0 0 1
1679 This is an internal function.
1681 @param DirectionUp the up or down direction. False is down. True is
1683 @param CurrentPosition Current position. On return: Point to the last
1684 Option (Year or Second) if up; Point to the first
1685 Option (Month or Hour) if down.
1687 @return Return line number to pad. It is possible that we stand on a zero-advance
1688 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1692 AdjustDateAndTimePosition (
1693 IN BOOLEAN DirectionUp
,
1694 IN OUT LIST_ENTRY
**CurrentPosition
1698 LIST_ENTRY
*NewPosition
;
1699 UI_MENU_OPTION
*MenuOption
;
1700 UINTN PadLineNumber
;
1703 NewPosition
= *CurrentPosition
;
1704 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1706 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
1707 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
1709 // Calculate the distance from current position to the last Date/Time MenuOption
1712 while (MenuOption
->Skip
== 0) {
1714 NewPosition
= NewPosition
->ForwardLink
;
1715 MenuOption
= MENU_OPTION_FROM_LINK (NewPosition
);
1719 NewPosition
= *CurrentPosition
;
1722 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1723 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1724 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1725 // checking can be done.
1727 while (Count
++ < 2) {
1728 NewPosition
= NewPosition
->BackLink
;
1732 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1733 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1734 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1735 // checking can be done.
1737 while (Count
-- > 0) {
1738 NewPosition
= NewPosition
->ForwardLink
;
1742 *CurrentPosition
= NewPosition
;
1745 return PadLineNumber
;
1749 Find HII Handle in the HII database associated with given Device Path.
1751 If DevicePath is NULL, then ASSERT.
1753 @param DevicePath Device Path associated with the HII package list
1756 @retval Handle HII package list Handle associated with the Device
1758 @retval NULL Hii Package list handle is not found.
1763 DevicePathToHiiHandle (
1764 IN EFI_DEVICE_PATH_PROTOCOL
*DevicePath
1768 EFI_DEVICE_PATH_PROTOCOL
*TmpDevicePath
;
1773 EFI_HANDLE DriverHandle
;
1774 EFI_HII_HANDLE
*HiiHandles
;
1775 EFI_HII_HANDLE HiiHandle
;
1777 ASSERT (DevicePath
!= NULL
);
1779 TmpDevicePath
= DevicePath
;
1781 // Locate Device Path Protocol handle buffer
1783 Status
= gBS
->LocateDevicePath (
1784 &gEfiDevicePathProtocolGuid
,
1788 if (EFI_ERROR (Status
) || !IsDevicePathEnd (TmpDevicePath
)) {
1793 // Retrieve all HII Handles from HII database
1795 BufferSize
= 0x1000;
1796 HiiHandles
= AllocatePool (BufferSize
);
1797 ASSERT (HiiHandles
!= NULL
);
1798 Status
= mHiiDatabase
->ListPackageLists (
1800 EFI_HII_PACKAGE_TYPE_ALL
,
1805 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1806 FreePool (HiiHandles
);
1807 HiiHandles
= AllocatePool (BufferSize
);
1808 ASSERT (HiiHandles
!= NULL
);
1810 Status
= mHiiDatabase
->ListPackageLists (
1812 EFI_HII_PACKAGE_TYPE_ALL
,
1819 if (EFI_ERROR (Status
)) {
1820 FreePool (HiiHandles
);
1825 // Search Hii Handle by Driver Handle
1828 HandleCount
= BufferSize
/ sizeof (EFI_HII_HANDLE
);
1829 for (Index
= 0; Index
< HandleCount
; Index
++) {
1830 Status
= mHiiDatabase
->GetPackageListHandle (
1835 if (!EFI_ERROR (Status
) && (Handle
== DriverHandle
)) {
1836 HiiHandle
= HiiHandles
[Index
];
1841 FreePool (HiiHandles
);
1846 Find HII Handle in the HII database associated with given form set guid.
1848 If FormSetGuid is NULL, then ASSERT.
1850 @param ComparingGuid FormSet Guid associated with the HII package list
1853 @retval Handle HII package list Handle associated with the Device
1855 @retval NULL Hii Package list handle is not found.
1859 FormSetGuidToHiiHandle (
1860 EFI_GUID
*ComparingGuid
1863 EFI_HII_HANDLE
*HiiHandles
;
1865 EFI_HII_PACKAGE_LIST_HEADER
*HiiPackageList
;
1869 UINT32 PackageListLength
;
1870 EFI_HII_PACKAGE_HEADER PackageHeader
;
1874 EFI_HII_HANDLE HiiHandle
;
1876 ASSERT (ComparingGuid
!= NULL
);
1880 // Get all the Hii handles
1882 HiiHandles
= HiiGetHiiHandles (NULL
);
1883 ASSERT (HiiHandles
!= NULL
);
1886 // Search for formset of each class type
1888 for (Index
= 0; HiiHandles
[Index
] != NULL
; Index
++) {
1890 HiiPackageList
= NULL
;
1891 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1892 if (Status
== EFI_BUFFER_TOO_SMALL
) {
1893 HiiPackageList
= AllocatePool (BufferSize
);
1894 ASSERT (HiiPackageList
!= NULL
);
1896 Status
= mHiiDatabase
->ExportPackageLists (mHiiDatabase
, HiiHandles
[Index
], &BufferSize
, HiiPackageList
);
1898 if (EFI_ERROR (Status
) || HiiPackageList
== NULL
) {
1903 // Get Form package from this HII package List
1905 Offset
= sizeof (EFI_HII_PACKAGE_LIST_HEADER
);
1907 CopyMem (&PackageListLength
, &HiiPackageList
->PackageLength
, sizeof (UINT32
));
1909 while (Offset
< PackageListLength
) {
1910 Package
= ((UINT8
*) HiiPackageList
) + Offset
;
1911 CopyMem (&PackageHeader
, Package
, sizeof (EFI_HII_PACKAGE_HEADER
));
1913 if (PackageHeader
.Type
== EFI_HII_PACKAGE_FORMS
) {
1915 // Search FormSet in this Form Package
1917 Offset2
= sizeof (EFI_HII_PACKAGE_HEADER
);
1918 while (Offset2
< PackageHeader
.Length
) {
1919 OpCodeData
= Package
+ Offset2
;
1921 if (((EFI_IFR_OP_HEADER
*) OpCodeData
)->OpCode
== EFI_IFR_FORM_SET_OP
) {
1923 // Try to compare against formset GUID
1925 if (CompareGuid (ComparingGuid
, (EFI_GUID
*)(OpCodeData
+ sizeof (EFI_IFR_OP_HEADER
)))) {
1926 HiiHandle
= HiiHandles
[Index
];
1931 Offset2
+= ((EFI_IFR_OP_HEADER
*) OpCodeData
)->Length
;
1934 if (HiiHandle
!= NULL
) {
1937 Offset
+= PackageHeader
.Length
;
1940 FreePool (HiiPackageList
);
1941 if (HiiHandle
!= NULL
) {
1946 FreePool (HiiHandles
);
1952 Transfer the device path string to binary format.
1954 @param StringPtr The device path string info.
1956 @retval Device path binary info.
1959 EFI_DEVICE_PATH_PROTOCOL
*
1960 ConvertDevicePathFromText (
1961 IN CHAR16
*StringPtr
1965 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
1967 UINT8
*DevicePathBuffer
;
1971 ASSERT (StringPtr
!= NULL
);
1973 BufferSize
= StrLen (StringPtr
) / 2;
1974 DevicePath
= AllocatePool (BufferSize
);
1975 ASSERT (DevicePath
!= NULL
);
1978 // Convert from Device Path String to DevicePath Buffer in the reverse order.
1980 DevicePathBuffer
= (UINT8
*) DevicePath
;
1981 for (Index
= 0; StringPtr
[Index
] != L
'\0'; Index
++) {
1982 TemStr
[0] = StringPtr
[Index
];
1983 DigitUint8
= (UINT8
) StrHexToUint64 (TemStr
);
1984 if (DigitUint8
== 0 && TemStr
[0] != L
'0') {
1986 // Invalid Hex Char as the tail.
1990 if ((Index
& 1) == 0) {
1991 DevicePathBuffer
[Index
/2] = DigitUint8
;
1993 DevicePathBuffer
[Index
/2] = (UINT8
) ((DevicePathBuffer
[Index
/2] << 4) + DigitUint8
);
2001 Process the goto op code, update the info in the selection structure.
2003 @param Statement The statement belong to goto op code.
2004 @param Selection The selection info.
2005 @param Repaint Whether need to repaint the menu.
2006 @param NewLine Whether need to create new line.
2008 @retval EFI_SUCCESS The menu process successfully.
2009 @return Other value if the process failed.
2013 IN OUT FORM_BROWSER_STATEMENT
*Statement
,
2014 IN OUT UI_MENU_SELECTION
*Selection
,
2015 OUT BOOLEAN
*Repaint
,
2016 OUT BOOLEAN
*NewLine
2020 EFI_DEVICE_PATH_PROTOCOL
*DevicePath
;
2021 FORM_BROWSER_FORM
*RefForm
;
2024 UI_MENU_LIST
*MenuList
;
2025 BOOLEAN UpdateFormInfo
;
2027 Status
= EFI_SUCCESS
;
2028 UpdateFormInfo
= TRUE
;
2032 // Prepare the device path check, get the device path info first.
2034 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0) {
2035 StringPtr
= GetToken (Statement
->HiiValue
.Value
.ref
.DevicePath
, Selection
->FormSet
->HiiHandle
);
2039 // Check whether the device path string is a valid string.
2041 if (Statement
->HiiValue
.Value
.ref
.DevicePath
!= 0 && StringPtr
!= NULL
) {
2042 if (Selection
->Form
->ModalForm
) {
2046 // Goto another Hii Package list
2048 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2049 DevicePath
= ConvertDevicePathFromText (StringPtr
);
2051 Selection
->Handle
= DevicePathToHiiHandle (DevicePath
);
2052 FreePool (DevicePath
);
2053 FreePool (StringPtr
);
2055 if (Selection
->Handle
== NULL
) {
2057 // If target Hii Handle not found, exit
2059 Selection
->Action
= UI_ACTION_EXIT
;
2060 Selection
->Statement
= NULL
;
2064 CopyMem (&Selection
->FormSetGuid
,&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2065 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2066 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2067 } else if (!CompareGuid (&Statement
->HiiValue
.Value
.ref
.FormSetGuid
, &gZeroGuid
)) {
2068 if (Selection
->Form
->ModalForm
) {
2072 // Goto another Formset, check for uncommitted data
2074 Selection
->Action
= UI_ACTION_REFRESH_FORMSET
;
2076 Selection
->Handle
= FormSetGuidToHiiHandle(&Statement
->HiiValue
.Value
.ref
.FormSetGuid
);
2077 if (Selection
->Handle
== NULL
) {
2079 // If target Hii Handle not found, exit
2081 Selection
->Action
= UI_ACTION_EXIT
;
2082 Selection
->Statement
= NULL
;
2086 CopyMem (&Selection
->FormSetGuid
, &Statement
->HiiValue
.Value
.ref
.FormSetGuid
, sizeof (EFI_GUID
));
2087 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2088 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2089 } else if (Statement
->HiiValue
.Value
.ref
.FormId
!= 0) {
2091 // Check whether target From is suppressed.
2093 RefForm
= IdToForm (Selection
->FormSet
, Statement
->HiiValue
.Value
.ref
.FormId
);
2095 if ((RefForm
!= NULL
) && (RefForm
->SuppressExpression
!= NULL
)) {
2096 if (EvaluateExpressionList(RefForm
->SuppressExpression
, TRUE
, Selection
->FormSet
, RefForm
) != ExpressFalse
) {
2098 // Form is suppressed.
2101 CreateDialog (4, TRUE
, 0, NULL
, &Key
, gEmptyString
, gFormSuppress
, gPressEnter
, gEmptyString
);
2102 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
2103 if (Repaint
!= NULL
) {
2111 // Goto another form inside this formset,
2113 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2115 Selection
->FormId
= Statement
->HiiValue
.Value
.ref
.FormId
;
2116 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2117 } else if (Statement
->HiiValue
.Value
.ref
.QuestionId
!= 0) {
2119 // Goto another Question
2121 Selection
->QuestionId
= Statement
->HiiValue
.Value
.ref
.QuestionId
;
2123 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2124 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2126 if (Repaint
!= NULL
) {
2129 if (NewLine
!= NULL
) {
2133 UpdateFormInfo
= FALSE
;
2135 if ((Statement
->QuestionFlags
& EFI_IFR_FLAG_CALLBACK
) != 0) {
2136 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
2138 UpdateFormInfo
= FALSE
;
2141 if (UpdateFormInfo
) {
2143 // Link current form so that we can always go back when someone hits the ESC
2145 MenuList
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2146 if (MenuList
== NULL
&& Selection
->CurrentMenu
!= NULL
) {
2147 MenuList
= UiAddMenuList (Selection
->CurrentMenu
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2155 Display menu and wait for user to select one menu option, then return it.
2156 If AutoBoot is enabled, then if user doesn't select any option,
2157 after period of time, it will automatically return the first menu option.
2159 @param Selection Menu selection.
2161 @retval EFI_SUCESSS This function always return successfully for now.
2166 IN OUT UI_MENU_SELECTION
*Selection
2172 UINTN DistanceValue
;
2183 CHAR16
*OptionString
;
2184 CHAR16
*OutputString
;
2186 CHAR16
*HelpHeaderString
;
2187 CHAR16
*HelpBottomString
;
2193 BOOLEAN InitializedFlag
;
2198 LIST_ENTRY
*TopOfScreen
;
2199 LIST_ENTRY
*SavedListEntry
;
2200 UI_MENU_OPTION
*MenuOption
;
2201 UI_MENU_OPTION
*NextMenuOption
;
2202 UI_MENU_OPTION
*SavedMenuOption
;
2203 UI_MENU_OPTION
*PreviousMenuOption
;
2204 UI_CONTROL_FLAG ControlFlag
;
2205 EFI_SCREEN_DESCRIPTOR LocalScreen
;
2206 MENU_REFRESH_ENTRY
*MenuRefreshEntry
;
2207 MENU_REFRESH_ENTRY
*MenuUpdateEntry
;
2208 UI_SCREEN_OPERATION ScreenOperation
;
2209 UINT8 MinRefreshInterval
;
2211 FORM_BROWSER_STATEMENT
*Statement
;
2212 UI_MENU_LIST
*CurrentMenu
;
2213 UINTN ModalSkipColumn
;
2214 BROWSER_HOT_KEY
*HotKey
;
2215 UINTN HelpPageIndex
;
2216 UINTN HelpPageCount
;
2219 UINTN HelpHeaderLine
;
2220 UINTN HelpBottomLine
;
2221 BOOLEAN MultiHelpPage
;
2223 UINT16 EachLineWidth
;
2224 UINT16 HeaderLineWidth
;
2225 UINT16 BottomLineWidth
;
2227 CopyMem (&LocalScreen
, &gScreenDimensions
, sizeof (EFI_SCREEN_DESCRIPTOR
));
2229 Status
= EFI_SUCCESS
;
2231 HelpHeaderString
= NULL
;
2232 HelpBottomString
= NULL
;
2233 OptionString
= NULL
;
2234 ScreenOperation
= UiNoOperation
;
2236 MinRefreshInterval
= 0;
2244 MultiHelpPage
= FALSE
;
2246 HeaderLineWidth
= 0;
2247 BottomLineWidth
= 0;
2248 OutputString
= NULL
;
2253 MenuRefreshEntry
= gMenuRefreshHead
;
2255 NextMenuOption
= NULL
;
2256 PreviousMenuOption
= NULL
;
2257 SavedMenuOption
= NULL
;
2259 ModalSkipColumn
= (LocalScreen
.RightColumn
- LocalScreen
.LeftColumn
) / 6;
2261 ZeroMem (&Key
, sizeof (EFI_INPUT_KEY
));
2263 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) == FORMSET_CLASS_FRONT_PAGE
){
2264 TopRow
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2265 Row
= LocalScreen
.TopRow
+ FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2267 TopRow
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2268 Row
= LocalScreen
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
+ SCROLL_ARROW_HEIGHT
;
2271 if (Selection
->Form
->ModalForm
) {
2272 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
+ ModalSkipColumn
;
2274 Col
= LocalScreen
.LeftColumn
+ LEFT_SKIPPED_COLUMNS
;
2277 BottomRow
= LocalScreen
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- SCROLL_ARROW_HEIGHT
- 1;
2279 Selection
->TopRow
= TopRow
;
2280 Selection
->BottomRow
= BottomRow
;
2281 Selection
->PromptCol
= Col
;
2282 Selection
->OptionCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2283 Selection
->Statement
= NULL
;
2285 TopOfScreen
= gMenuOption
.ForwardLink
;
2290 // Find current Menu
2292 CurrentMenu
= UiFindMenuList (&Selection
->FormSetGuid
, Selection
->FormId
);
2293 if (CurrentMenu
== NULL
) {
2295 // Current menu not found, add it to the menu tree
2297 CurrentMenu
= UiAddMenuList (NULL
, Selection
->Handle
, &Selection
->FormSetGuid
, Selection
->FormId
);
2299 ASSERT (CurrentMenu
!= NULL
);
2300 Selection
->CurrentMenu
= CurrentMenu
;
2302 if (Selection
->QuestionId
== 0) {
2304 // Highlight not specified, fetch it from cached menu
2306 Selection
->QuestionId
= CurrentMenu
->QuestionId
;
2307 Selection
->Sequence
= CurrentMenu
->Sequence
;
2311 // Init option as the current user's selection
2313 InitializedFlag
= TRUE
;
2314 NewPos
= gMenuOption
.ForwardLink
;
2316 gST
->ConOut
->EnableCursor (gST
->ConOut
, FALSE
);
2317 UpdateStatusBar (Selection
, REFRESH_STATUS_BAR
, (UINT8
) 0, TRUE
);
2319 ControlFlag
= CfInitialization
;
2320 Selection
->Action
= UI_ACTION_NONE
;
2322 switch (ControlFlag
) {
2323 case CfInitialization
:
2324 if (IsListEmpty (&gMenuOption
)) {
2325 ControlFlag
= CfReadKey
;
2327 ControlFlag
= CfCheckSelection
;
2331 case CfCheckSelection
:
2332 if (Selection
->Action
!= UI_ACTION_NONE
) {
2333 ControlFlag
= CfExit
;
2335 ControlFlag
= CfRepaint
;
2340 ControlFlag
= CfRefreshHighLight
;
2350 Temp
= (UINTN
) SkipValue
;
2351 Temp2
= (UINTN
) SkipValue
;
2353 if (Selection
->Form
->ModalForm
) {
2355 LocalScreen
.LeftColumn
+ ModalSkipColumn
,
2356 LocalScreen
.LeftColumn
+ ModalSkipColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
,
2357 TopRow
- SCROLL_ARROW_HEIGHT
,
2358 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2359 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2363 LocalScreen
.LeftColumn
,
2364 LocalScreen
.RightColumn
,
2365 TopRow
- SCROLL_ARROW_HEIGHT
,
2366 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2367 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
2370 UiFreeRefreshList ();
2371 MinRefreshInterval
= 0;
2373 for (Link
= TopOfScreen
; Link
!= &gMenuOption
; Link
= Link
->ForwardLink
) {
2374 MenuOption
= MENU_OPTION_FROM_LINK (Link
);
2375 MenuOption
->Row
= Row
;
2376 MenuOption
->Col
= Col
;
2377 if (Selection
->Form
->ModalForm
) {
2378 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
+ ModalSkipColumn
;
2380 MenuOption
->OptCol
= gPromptBlockWidth
+ 1 + LocalScreen
.LeftColumn
;
2383 Statement
= MenuOption
->ThisTag
;
2384 if (Statement
->InSubtitle
) {
2385 MenuOption
->Col
+= SUBTITLE_INDENT
;
2388 if (MenuOption
->GrayOut
) {
2389 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2391 if (Statement
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2392 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2396 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2400 if (Statement
->Operand
== EFI_IFR_REF_OP
&& MenuOption
->Col
>= 2) {
2402 // Print Arrow for Goto button.
2405 MenuOption
->Col
- 2,
2408 GEOMETRICSHAPE_RIGHT_TRIANGLE
2412 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2413 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2414 PrintStringAt (MenuOption
->Col
, Row
, OutputString
);
2417 // If there is more string to process print on the next row and increment the Skip value
2419 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2425 FreePool (OutputString
);
2434 Status
= ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2435 if (EFI_ERROR (Status
)) {
2437 // Repaint to clear possible error prompt pop-up
2441 ControlFlag
= CfRepaint
;
2445 if (OptionString
!= NULL
) {
2446 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2447 ProcessStringForDateTime(MenuOption
, OptionString
, TRUE
);
2450 Width
= (UINT16
) gOptionBlockWidth
;
2454 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2455 if ((Temp2
== 0) && (Row
<= BottomRow
)) {
2456 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2459 // If there is more string to process print on the next row and increment the Skip value
2461 if (StrLen (&OptionString
[Index
]) != 0) {
2465 // Since the Number of lines for this menu entry may or may not be reflected accurately
2466 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2467 // some testing to ensure we are keeping this in-sync.
2469 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2471 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2477 FreePool (OutputString
);
2486 FreePool (OptionString
);
2490 // If Question has refresh guid, register the op-code.
2492 if (!CompareGuid (&Statement
->RefreshGuid
, &gZeroGuid
)) {
2493 if (gMenuEventGuidRefreshHead
== NULL
) {
2494 MenuUpdateEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2495 gMenuEventGuidRefreshHead
= MenuUpdateEntry
;
2497 MenuUpdateEntry
= gMenuEventGuidRefreshHead
;
2498 while (MenuUpdateEntry
->Next
!= NULL
) {
2499 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2501 MenuUpdateEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2502 MenuUpdateEntry
= MenuUpdateEntry
->Next
;
2504 ASSERT (MenuUpdateEntry
!= NULL
);
2505 Status
= gBS
->CreateEventEx (EVT_NOTIFY_SIGNAL
, TPL_NOTIFY
, RefreshQuestionNotify
, MenuUpdateEntry
, &Statement
->RefreshGuid
, &MenuUpdateEntry
->Event
);
2506 ASSERT (!EFI_ERROR (Status
));
2507 MenuUpdateEntry
->MenuOption
= MenuOption
;
2508 MenuUpdateEntry
->Selection
= Selection
;
2509 MenuUpdateEntry
->CurrentColumn
= MenuOption
->OptCol
;
2510 MenuUpdateEntry
->CurrentRow
= MenuOption
->Row
;
2511 if (MenuOption
->GrayOut
) {
2512 MenuUpdateEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2514 MenuUpdateEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2519 // If Question request refresh, register the op-code
2521 if (Statement
->RefreshInterval
!= 0) {
2523 // Menu will be refreshed at minimal interval of all Questions
2524 // which have refresh request
2526 if (MinRefreshInterval
== 0 || Statement
->RefreshInterval
< MinRefreshInterval
) {
2527 MinRefreshInterval
= Statement
->RefreshInterval
;
2530 if (gMenuRefreshHead
== NULL
) {
2531 MenuRefreshEntry
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2532 gMenuRefreshHead
= MenuRefreshEntry
;
2534 MenuRefreshEntry
= gMenuRefreshHead
;
2535 while (MenuRefreshEntry
->Next
!= NULL
) {
2536 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2538 MenuRefreshEntry
->Next
= AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY
));
2539 MenuRefreshEntry
= MenuRefreshEntry
->Next
;
2541 ASSERT (MenuRefreshEntry
!= NULL
);
2542 MenuRefreshEntry
->MenuOption
= MenuOption
;
2543 MenuRefreshEntry
->Selection
= Selection
;
2544 MenuRefreshEntry
->CurrentColumn
= MenuOption
->OptCol
;
2545 MenuRefreshEntry
->CurrentRow
= MenuOption
->Row
;
2546 if (MenuOption
->GrayOut
) {
2547 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2549 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2554 // If this is a text op with secondary text information
2556 if ((Statement
->Operand
== EFI_IFR_TEXT_OP
) && (Statement
->TextTwo
!= 0)) {
2557 StringPtr
= GetToken (Statement
->TextTwo
, MenuOption
->Handle
);
2559 Width
= (UINT16
) gOptionBlockWidth
;
2563 for (Index
= 0; GetLineByWidth (StringPtr
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2564 if ((Temp
== 0) && (Row
<= BottomRow
)) {
2565 PrintStringAt (MenuOption
->OptCol
, Row
, OutputString
);
2568 // If there is more string to process print on the next row and increment the Skip value
2570 if (StrLen (&StringPtr
[Index
]) != 0) {
2574 // Since the Number of lines for this menu entry may or may not be reflected accurately
2575 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2576 // some testing to ensure we are keeping this in-sync.
2578 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2580 if ((Row
- OriginalRow
) >= MenuOption
->Skip
) {
2586 FreePool (OutputString
);
2593 FreePool (StringPtr
);
2595 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2598 // Need to handle the bottom of the display
2600 if (MenuOption
->Skip
> 1) {
2601 Row
+= MenuOption
->Skip
- SkipValue
;
2604 Row
+= MenuOption
->Skip
;
2607 if (Row
> BottomRow
) {
2608 if (!ValueIsScroll (FALSE
, Link
)) {
2612 Row
= BottomRow
+ 1;
2617 if (!ValueIsScroll (TRUE
, TopOfScreen
)) {
2622 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2624 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2625 TopRow
- SCROLL_ARROW_HEIGHT
,
2629 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2633 gST
->ConOut
->SetAttribute (gST
->ConOut
, ARROW_TEXT
| ARROW_BACKGROUND
);
2635 LocalScreen
.LeftColumn
+ gPromptBlockWidth
+ gOptionBlockWidth
+ 1,
2636 BottomRow
+ SCROLL_ARROW_HEIGHT
,
2640 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2647 case CfRefreshHighLight
:
2649 // MenuOption: Last menu option that need to remove hilight
2650 // MenuOption is set to NULL in Repaint
2651 // NewPos: Current menu option that need to hilight
2653 ControlFlag
= CfUpdateHelpString
;
2654 if (InitializedFlag
) {
2655 InitializedFlag
= FALSE
;
2656 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
2660 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2661 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2663 SavedValue
= Repaint
;
2666 if (Selection
->QuestionId
!= 0) {
2667 NewPos
= gMenuOption
.ForwardLink
;
2668 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2670 while ((SavedMenuOption
->ThisTag
->QuestionId
!= Selection
->QuestionId
||
2671 SavedMenuOption
->Sequence
!= Selection
->Sequence
) &&
2672 NewPos
->ForwardLink
!= &gMenuOption
) {
2673 NewPos
= NewPos
->ForwardLink
;
2674 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2676 if (SavedMenuOption
->ThisTag
->QuestionId
== Selection
->QuestionId
) {
2678 // Target Question found, find its MenuOption
2682 for (Index
= TopRow
; Index
<= BottomRow
&& Link
!= NewPos
;) {
2683 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2684 Index
+= SavedMenuOption
->Skip
;
2685 if (Link
== TopOfScreen
) {
2686 Index
-= OldSkipValue
;
2688 Link
= Link
->ForwardLink
;
2690 if (NewPos
== Link
) {
2691 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2695 // Not find the selected menu in current show page.
2696 // Have two case to enter this if:
2697 // 1. Not find the menu at current page.
2698 // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
2699 // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
2701 // Base on the selected menu will show at the bottom of the page,
2702 // select the menu which will show at the top of the page.
2704 if (Link
!= NewPos
|| Index
> BottomRow
||
2705 (Link
== NewPos
&& (SavedMenuOption
->Row
+ SavedMenuOption
->Skip
- 1 > BottomRow
) && (Link
!= TopOfScreen
))) {
2707 // Find the MenuOption which has the skip value for Date/Time opcode.
2709 AdjustDateAndTimePosition(FALSE
, &NewPos
);
2711 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2713 SavedMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2715 // SavedMenuOption->Row == 0 means the menu not show yet.
2717 if (SavedMenuOption
->Row
== 0) {
2718 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2722 // Base on the selected menu will show at the bottome of next page,
2723 // select the menu show at the top of the next page.
2726 for (Index
= TopRow
+ SavedMenuOption
->Skip
; Index
<= BottomRow
+ 1; ) {
2727 Link
= Link
->BackLink
;
2728 SavedMenuOption
= MENU_OPTION_FROM_LINK (Link
);
2729 if (SavedMenuOption
->Row
== 0) {
2730 UpdateOptionSkipLines (Selection
, SavedMenuOption
);
2732 Index
+= SavedMenuOption
->Skip
;
2736 // Found the menu which will show at the top of the page.
2738 if (Link
== NewPos
) {
2740 // The menu can show more than one pages, just show the menu at the top of the page.
2744 OldSkipValue
= SkipValue
;
2747 // Check whether need to skip some line for menu shows at the top of the page.
2749 SkipValue
= Index
- BottomRow
- 1;
2750 if (SkipValue
> 0 && SkipValue
< (INTN
) SavedMenuOption
->Skip
) {
2752 OldSkipValue
= SkipValue
;
2755 TopOfScreen
= Link
->ForwardLink
;
2761 ControlFlag
= CfRepaint
;
2766 // Target Question not found, highlight the default menu option
2768 NewPos
= TopOfScreen
;
2771 Selection
->QuestionId
= 0;
2774 if (NewPos
!= NULL
&& (MenuOption
== NULL
|| NewPos
!= &MenuOption
->Link
)) {
2775 if (MenuOption
!= NULL
) {
2777 // Remove highlight on last Menu Option
2779 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2780 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2781 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2782 if (OptionString
!= NULL
) {
2783 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) ||
2784 (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
2786 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2789 Width
= (UINT16
) gOptionBlockWidth
;
2790 OriginalRow
= MenuOption
->Row
;
2793 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2794 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2795 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2798 // If there is more string to process print on the next row and increment the Skip value
2800 if (StrLen (&OptionString
[Index
]) != 0) {
2804 FreePool (OutputString
);
2807 MenuOption
->Row
= OriginalRow
;
2809 FreePool (OptionString
);
2812 if (MenuOption
->GrayOut
) {
2813 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
);
2814 } else if (MenuOption
->ThisTag
->Operand
== EFI_IFR_SUBTITLE_OP
) {
2815 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserSubtitleTextColor
) | FIELD_BACKGROUND
);
2818 OriginalRow
= MenuOption
->Row
;
2819 Width
= GetWidth (MenuOption
->ThisTag
, MenuOption
->Handle
);
2822 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2823 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2824 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2827 // If there is more string to process print on the next row and increment the Skip value
2829 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2833 FreePool (OutputString
);
2836 MenuOption
->Row
= OriginalRow
;
2837 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2843 // This is the current selected statement
2845 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
2846 Statement
= MenuOption
->ThisTag
;
2847 Selection
->Statement
= Statement
;
2848 if (!IsSelectable (MenuOption
)) {
2849 Repaint
= SavedValue
;
2850 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2855 // Record highlight for current menu
2857 CurrentMenu
->QuestionId
= Statement
->QuestionId
;
2858 CurrentMenu
->Sequence
= MenuOption
->Sequence
;
2861 // Set reverse attribute
2863 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
2864 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, MenuOption
->Col
, MenuOption
->Row
);
2867 // Assuming that we have a refresh linked-list created, lets annotate the
2868 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2869 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2871 if (gMenuRefreshHead
!= NULL
) {
2872 for (MenuRefreshEntry
= gMenuRefreshHead
; MenuRefreshEntry
!= NULL
; MenuRefreshEntry
= MenuRefreshEntry
->Next
) {
2873 if (MenuRefreshEntry
->MenuOption
->GrayOut
) {
2874 MenuRefreshEntry
->CurrentAttribute
= FIELD_TEXT_GRAYED
| FIELD_BACKGROUND
;
2876 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
;
2878 if (MenuRefreshEntry
->MenuOption
== MenuOption
) {
2879 MenuRefreshEntry
->CurrentAttribute
= PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
);
2884 ProcessOptions (Selection
, MenuOption
, FALSE
, &OptionString
);
2885 if (OptionString
!= NULL
) {
2886 if (Statement
->Operand
== EFI_IFR_DATE_OP
|| Statement
->Operand
== EFI_IFR_TIME_OP
) {
2887 ProcessStringForDateTime(MenuOption
, OptionString
, FALSE
);
2889 Width
= (UINT16
) gOptionBlockWidth
;
2891 OriginalRow
= MenuOption
->Row
;
2894 for (Index
= 0; GetLineByWidth (OptionString
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2895 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2896 PrintStringAt (MenuOption
->OptCol
, MenuOption
->Row
, OutputString
);
2899 // If there is more string to process print on the next row and increment the Skip value
2901 if (StrLen (&OptionString
[Index
]) != 0) {
2905 FreePool (OutputString
);
2908 MenuOption
->Row
= OriginalRow
;
2910 FreePool (OptionString
);
2913 OriginalRow
= MenuOption
->Row
;
2915 Width
= GetWidth (Statement
, MenuOption
->Handle
);
2918 for (Index
= 0; GetLineByWidth (MenuOption
->Description
, Width
, &GlyphWidth
, &Index
, &OutputString
) != 0x0000;) {
2919 if (MenuOption
->Row
>= TopRow
&& MenuOption
->Row
<= BottomRow
) {
2920 PrintStringAt (MenuOption
->Col
, MenuOption
->Row
, OutputString
);
2923 // If there is more string to process print on the next row and increment the Skip value
2925 if (StrLen (&MenuOption
->Description
[Index
]) != 0) {
2929 FreePool (OutputString
);
2932 MenuOption
->Row
= OriginalRow
;
2937 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
2940 // Clear reverse attribute
2942 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
2945 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2946 // if we didn't break halfway when process CfRefreshHighLight.
2948 Repaint
= SavedValue
;
2951 case CfUpdateHelpString
:
2952 ControlFlag
= CfPrepareToReadKey
;
2953 if (Selection
->Form
->ModalForm
) {
2957 if (Repaint
|| NewLine
) {
2959 // Don't print anything if it is a NULL help token
2961 ASSERT(MenuOption
!= NULL
);
2962 if (MenuOption
->ThisTag
->Help
== 0 || !IsSelectable (MenuOption
)) {
2965 StringPtr
= GetToken (MenuOption
->ThisTag
->Help
, MenuOption
->Handle
);
2968 RowCount
= BottomRow
- TopRow
;
2971 // 1.Calculate how many line the help string need to print.
2973 if (HelpString
!= NULL
) {
2974 FreePool (HelpString
);
2976 HelpLine
= ProcessHelpString (StringPtr
, &HelpString
, &EachLineWidth
, RowCount
);
2977 if (HelpLine
> RowCount
) {
2978 MultiHelpPage
= TRUE
;
2979 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP
), gHiiHandle
);
2980 if (HelpHeaderString
!= NULL
) {
2981 FreePool (HelpHeaderString
);
2983 HelpHeaderLine
= ProcessHelpString (StringPtr
, &HelpHeaderString
, &HeaderLineWidth
, RowCount
);
2984 StringPtr
= GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN
), gHiiHandle
);
2985 if (HelpBottomString
!= NULL
) {
2986 FreePool (HelpBottomString
);
2988 HelpBottomLine
= ProcessHelpString (StringPtr
, &HelpBottomString
, &BottomLineWidth
, RowCount
);
2990 // Calculate the help page count.
2992 if (HelpLine
> 2 * RowCount
- 2) {
2993 HelpPageCount
= (HelpLine
- RowCount
+ 1) / (RowCount
- 2) + 1;
2994 if ((HelpLine
- RowCount
+ 1) % (RowCount
- 2) > 1) {
3001 MultiHelpPage
= FALSE
;
3006 // Clean the help field first.
3009 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3010 LocalScreen
.RightColumn
,
3013 PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
3017 // Check whether need to show the 'More(U/u)' at the begin.
3018 // Base on current direct info, here shows aligned to the right side of the column.
3019 // If the direction is multi line and aligned to right side may have problem, so
3020 // add ASSERT code here.
3022 if (HelpPageIndex
> 0) {
3023 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3024 for (Index
= 0; Index
< HelpHeaderLine
; Index
++) {
3025 ASSERT (HelpHeaderLine
== 1);
3026 ASSERT (GetStringWidth (HelpHeaderString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3028 LocalScreen
.RightColumn
- GetStringWidth (HelpHeaderString
) / 2 - 1,
3030 &HelpHeaderString
[Index
* HeaderLineWidth
]
3035 gST
->ConOut
->SetAttribute (gST
->ConOut
, HELP_TEXT
| FIELD_BACKGROUND
);
3037 // Print the help string info.
3039 if (!MultiHelpPage
) {
3040 for (Index
= 0; Index
< HelpLine
; Index
++) {
3042 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3044 &HelpString
[Index
* EachLineWidth
]
3047 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3049 if (HelpPageIndex
== 0) {
3050 for (Index
= 0; Index
< RowCount
- HelpBottomLine
; Index
++) {
3052 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3054 &HelpString
[Index
* EachLineWidth
]
3058 for (Index
= 0; (Index
< RowCount
- HelpBottomLine
- HelpHeaderLine
) &&
3059 (Index
+ HelpPageIndex
* (RowCount
- 2) + 1 < HelpLine
); Index
++) {
3061 LocalScreen
.RightColumn
- gHelpBlockWidth
,
3062 Index
+ TopRow
+ HelpHeaderLine
,
3063 &HelpString
[(Index
+ HelpPageIndex
* (RowCount
- 2) + 1)* EachLineWidth
]
3066 if (HelpPageIndex
== HelpPageCount
- 1) {
3067 gST
->ConOut
->SetCursorPosition(gST
->ConOut
, LocalScreen
.RightColumn
-1, BottomRow
);
3073 // Check whether need to print the 'More(D/d)' at the bottom.
3074 // Base on current direct info, here shows aligned to the right side of the column.
3075 // If the direction is multi line and aligned to right side may have problem, so
3076 // add ASSERT code here.
3078 if (HelpPageIndex
< HelpPageCount
- 1 && MultiHelpPage
) {
3079 gST
->ConOut
->SetAttribute (gST
->ConOut
, INFO_TEXT
| FIELD_BACKGROUND
);
3080 for (Index
= 0; Index
< HelpBottomLine
; Index
++) {
3081 ASSERT (HelpBottomLine
== 1);
3082 ASSERT (GetStringWidth (HelpBottomString
) / 2 < (UINTN
) (gHelpBlockWidth
- 1));
3084 LocalScreen
.RightColumn
- GetStringWidth (HelpBottomString
) / 2 - 1,
3085 Index
+ BottomRow
- HelpBottomLine
,
3086 &HelpBottomString
[Index
* BottomLineWidth
]
3091 // Reset this flag every time we finish using it.
3097 case CfPrepareToReadKey
:
3098 ControlFlag
= CfReadKey
;
3099 ScreenOperation
= UiNoOperation
;
3103 ControlFlag
= CfScreenOperation
;
3106 // Wait for user's selection
3109 Status
= UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, MinRefreshInterval
);
3110 } while (Status
== EFI_TIMEOUT
);
3112 if (Selection
->Action
== UI_ACTION_REFRESH_FORMSET
) {
3114 // IFR is updated in Callback of refresh opcode, re-parse it
3116 ControlFlag
= CfCheckSelection
;
3117 Selection
->Statement
= NULL
;
3121 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, &Key
);
3123 // If we encounter error, continue to read another key in.
3125 if (EFI_ERROR (Status
)) {
3126 ControlFlag
= CfReadKey
;
3130 switch (Key
.UnicodeChar
) {
3131 case CHAR_CARRIAGE_RETURN
:
3132 if(MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3133 ControlFlag
= CfReadKey
;
3137 ScreenOperation
= UiSelect
;
3142 // We will push the adjustment of these numeric values directly to the input handler
3143 // NOTE: we won't handle manual input numeric
3148 // If the screen has no menu items, and the user didn't select UiReset
3149 // ignore the selection and go back to reading keys.
3151 if(IsListEmpty (&gMenuOption
) || MenuOption
->GrayOut
|| MenuOption
->ReadOnly
) {
3152 ControlFlag
= CfReadKey
;
3156 ASSERT(MenuOption
!= NULL
);
3157 Statement
= MenuOption
->ThisTag
;
3158 if ((Statement
->Operand
== EFI_IFR_DATE_OP
)
3159 || (Statement
->Operand
== EFI_IFR_TIME_OP
)
3160 || ((Statement
->Operand
== EFI_IFR_NUMERIC_OP
) && (Statement
->Step
!= 0))
3162 if (Key
.UnicodeChar
== '+') {
3163 gDirection
= SCAN_RIGHT
;
3165 gDirection
= SCAN_LEFT
;
3167 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3168 if (EFI_ERROR (Status
)) {
3170 // Repaint to clear possible error prompt pop-up
3175 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3177 if (OptionString
!= NULL
) {
3178 FreePool (OptionString
);
3184 ScreenOperation
= UiUp
;
3189 ScreenOperation
= UiDown
;
3193 if ((gClassOfVfr
& FORMSET_CLASS_FRONT_PAGE
) != FORMSET_CLASS_FRONT_PAGE
) {
3195 // If the screen has no menu items, and the user didn't select UiReset
3196 // ignore the selection and go back to reading keys.
3198 if(IsListEmpty (&gMenuOption
)) {
3199 ControlFlag
= CfReadKey
;
3203 ASSERT(MenuOption
!= NULL
);
3204 if (MenuOption
->ThisTag
->Operand
== EFI_IFR_CHECKBOX_OP
&& !MenuOption
->GrayOut
&& !MenuOption
->ReadOnly
) {
3205 ScreenOperation
= UiSelect
;
3212 if (!MultiHelpPage
) {
3213 ControlFlag
= CfReadKey
;
3216 ControlFlag
= CfUpdateHelpString
;
3217 HelpPageIndex
= HelpPageIndex
< HelpPageCount
- 1 ? HelpPageIndex
+ 1 : HelpPageCount
- 1;
3222 if (!MultiHelpPage
) {
3223 ControlFlag
= CfReadKey
;
3226 ControlFlag
= CfUpdateHelpString
;
3227 HelpPageIndex
= HelpPageIndex
> 0 ? HelpPageIndex
- 1 : 0;
3231 for (Index
= 0; Index
< mScanCodeNumber
; Index
++) {
3232 if (Key
.ScanCode
== gScanCodeToOperation
[Index
].ScanCode
) {
3233 ScreenOperation
= gScanCodeToOperation
[Index
].ScreenOperation
;
3238 if (Selection
->Form
->ModalForm
&& (Key
.ScanCode
== SCAN_ESC
|| Index
== mScanCodeNumber
)) {
3240 // ModalForm has no ESC key and Hot Key.
3242 ControlFlag
= CfReadKey
;
3243 } else if (Index
== mScanCodeNumber
) {
3245 // Check whether Key matches the registered hot key.
3248 if ((gBrowserSettingScope
== SystemLevel
) || (gFunctionKeySetting
!= NONE_FUNCTION_KEY_SETTING
)) {
3249 HotKey
= GetHotKeyFromRegisterList (&Key
);
3251 if (HotKey
!= NULL
) {
3252 ScreenOperation
= UiHotKey
;
3259 case CfScreenOperation
:
3260 if (ScreenOperation
!= UiReset
) {
3262 // If the screen has no menu items, and the user didn't select UiReset
3263 // ignore the selection and go back to reading keys.
3265 if (IsListEmpty (&gMenuOption
)) {
3266 ControlFlag
= CfReadKey
;
3272 Index
< sizeof (gScreenOperationToControlFlag
) / sizeof (gScreenOperationToControlFlag
[0]);
3275 if (ScreenOperation
== gScreenOperationToControlFlag
[Index
].ScreenOperation
) {
3276 ControlFlag
= gScreenOperationToControlFlag
[Index
].ControlFlag
;
3283 ControlFlag
= CfCheckSelection
;
3285 ASSERT(MenuOption
!= NULL
);
3286 Statement
= MenuOption
->ThisTag
;
3287 if (Statement
->Operand
== EFI_IFR_TEXT_OP
) {
3292 // Keep highlight on current MenuOption
3294 Selection
->QuestionId
= Statement
->QuestionId
;
3296 switch (Statement
->Operand
) {
3297 case EFI_IFR_REF_OP
:
3298 ProcessGotoOpCode(Statement
, Selection
, &Repaint
, &NewLine
);
3301 case EFI_IFR_ACTION_OP
:
3303 // Process the Config string <ConfigResp>
3305 Status
= ProcessQuestionConfig (Selection
, Statement
);
3307 if (EFI_ERROR (Status
)) {
3312 // The action button may change some Question value, so refresh the form
3314 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3317 case EFI_IFR_RESET_BUTTON_OP
:
3319 // Reset Question to default value specified by DefaultId
3321 ControlFlag
= CfUiDefault
;
3322 DefaultId
= Statement
->DefaultId
;
3327 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3329 UpdateKeyHelp (Selection
, MenuOption
, TRUE
);
3330 Status
= ProcessOptions (Selection
, MenuOption
, TRUE
, &OptionString
);
3332 if (EFI_ERROR (Status
)) {
3335 UpdateKeyHelp (Selection
, MenuOption
, FALSE
);
3337 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3340 if (OptionString
!= NULL
) {
3341 FreePool (OptionString
);
3349 // We come here when someone press ESC
3351 ControlFlag
= CfCheckSelection
;
3352 FindNextMenu (Selection
, &Repaint
, &NewLine
);
3356 ControlFlag
= CfCheckSelection
;
3357 ASSERT(MenuOption
!= NULL
);
3358 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3359 if (MenuOption
->Sequence
!= 0) {
3361 // In the middle or tail of the Date/Time op-code set, go left.
3363 ASSERT(NewPos
!= NULL
);
3364 NewPos
= NewPos
->BackLink
;
3370 ControlFlag
= CfCheckSelection
;
3371 ASSERT(MenuOption
!= NULL
);
3372 if ((MenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
) || (MenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)) {
3373 if (MenuOption
->Sequence
!= 2) {
3375 // In the middle or tail of the Date/Time op-code set, go left.
3377 ASSERT(NewPos
!= NULL
);
3378 NewPos
= NewPos
->ForwardLink
;
3384 ControlFlag
= CfCheckSelection
;
3386 SavedListEntry
= NewPos
;
3388 ASSERT(NewPos
!= NULL
);
3390 // Adjust Date/Time position before we advance forward.
3392 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3393 if (NewPos
->BackLink
!= &gMenuOption
) {
3394 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3395 ASSERT (MenuOption
!= NULL
);
3397 NewPos
= NewPos
->BackLink
;
3399 PreviousMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3400 if (PreviousMenuOption
->Row
== 0) {
3401 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3403 DistanceValue
= PreviousMenuOption
->Skip
;
3405 if (MenuOption
->Row
>= DistanceValue
+ TopRow
) {
3406 Difference
= MoveToNextStatement (Selection
, TRUE
, &NewPos
, MenuOption
->Row
- TopRow
- DistanceValue
);
3408 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3410 if (Difference
< 0) {
3412 // We hit the begining MenuOption that can be focused
3413 // so we simply scroll to the top.
3415 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3416 TopOfScreen
= gMenuOption
.ForwardLink
;
3420 // Scroll up to the last page when we have arrived at top page.
3422 NewPos
= &gMenuOption
;
3423 TopOfScreen
= &gMenuOption
;
3424 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3425 ScreenOperation
= UiPageUp
;
3426 ControlFlag
= CfScreenOperation
;
3429 } else if (MenuOption
->Row
< TopRow
+ DistanceValue
+ Difference
) {
3431 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3433 TopOfScreen
= NewPos
;
3437 } else if (!IsSelectable (NextMenuOption
)) {
3439 // Continue to go up until scroll to next page or the selectable option is found.
3441 ScreenOperation
= UiUp
;
3442 ControlFlag
= CfScreenOperation
;
3446 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3448 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3449 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3450 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3451 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3454 // Scroll up to the last page.
3456 NewPos
= &gMenuOption
;
3457 TopOfScreen
= &gMenuOption
;
3458 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3459 ScreenOperation
= UiPageUp
;
3460 ControlFlag
= CfScreenOperation
;
3465 ControlFlag
= CfCheckSelection
;
3467 ASSERT(NewPos
!= NULL
);
3468 if (NewPos
->BackLink
== &gMenuOption
) {
3478 while ((Index
>= TopRow
) && (Link
->BackLink
!= &gMenuOption
)) {
3479 Link
= Link
->BackLink
;
3480 PreviousMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3481 if (PreviousMenuOption
->Row
== 0) {
3482 UpdateOptionSkipLines (Selection
, PreviousMenuOption
);
3484 if (Index
< PreviousMenuOption
->Skip
) {
3488 Index
= Index
- PreviousMenuOption
->Skip
;
3491 if ((Link
->BackLink
== &gMenuOption
) && (Index
>= TopRow
)) {
3492 if (TopOfScreen
== &gMenuOption
) {
3493 TopOfScreen
= gMenuOption
.ForwardLink
;
3494 NewPos
= gMenuOption
.BackLink
;
3495 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3497 } else if (TopOfScreen
!= Link
) {
3500 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3503 // Finally we know that NewPos is the last MenuOption can be focused.
3507 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3510 if (Index
+ 1 < TopRow
) {
3512 // Back up the previous option.
3514 Link
= Link
->ForwardLink
;
3518 // Move to the option in Next page.
3520 if (TopOfScreen
== &gMenuOption
) {
3521 NewPos
= gMenuOption
.BackLink
;
3522 MoveToNextStatement (Selection
, TRUE
, &NewPos
, BottomRow
- TopRow
);
3525 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3529 // There are more MenuOption needing scrolling up.
3536 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3537 // Don't do this when we are already in the first page.
3539 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3540 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3544 ControlFlag
= CfCheckSelection
;
3546 ASSERT (NewPos
!= NULL
);
3547 if (NewPos
->ForwardLink
== &gMenuOption
) {
3556 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3558 while ((Index
<= BottomRow
) && (Link
->ForwardLink
!= &gMenuOption
)) {
3559 Index
= Index
+ NextMenuOption
->Skip
;
3560 Link
= Link
->ForwardLink
;
3561 NextMenuOption
= MENU_OPTION_FROM_LINK (Link
);
3564 if ((Link
->ForwardLink
== &gMenuOption
) && (Index
<= BottomRow
)) {
3566 // Finally we know that NewPos is the last MenuOption can be focused.
3569 MoveToNextStatement (Selection
, TRUE
, &Link
, Index
- TopRow
);
3571 if (Index
- 1 > BottomRow
) {
3573 // Back up the previous option.
3575 Link
= Link
->BackLink
;
3578 // There are more MenuOption needing scrolling down.
3583 // Move to the option in Next page.
3585 MoveToNextStatement (Selection
, FALSE
, &Link
, BottomRow
- TopRow
);
3589 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3590 // Don't do this when we are already in the last page.
3593 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3594 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3598 ControlFlag
= CfCheckSelection
;
3600 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3601 // to be one that progresses to the next set of op-codes, we need to advance to the last
3602 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3603 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3604 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3605 // the Date/Time op-code.
3607 SavedListEntry
= NewPos
;
3608 AdjustDateAndTimePosition (FALSE
, &NewPos
);
3610 if (NewPos
->ForwardLink
!= &gMenuOption
) {
3611 MenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3613 NewPos
= NewPos
->ForwardLink
;
3616 if (BottomRow
>= MenuOption
->Row
+ MenuOption
->Skip
) {
3617 Difference
= MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- MenuOption
->Row
- MenuOption
->Skip
);
3619 // We hit the end of MenuOption that can be focused
3620 // so we simply scroll to the first page.
3622 if (Difference
< 0) {
3624 // Scroll to the first page.
3626 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3627 TopOfScreen
= gMenuOption
.ForwardLink
;
3631 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3633 NewPos
= gMenuOption
.ForwardLink
;
3634 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3637 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3639 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3640 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3644 NextMenuOption
= MENU_OPTION_FROM_LINK (NewPos
);
3647 // An option might be multi-line, so we need to reflect that data in the overall skip value
3649 UpdateOptionSkipLines (Selection
, NextMenuOption
);
3650 DistanceValue
= Difference
+ NextMenuOption
->Skip
;
3652 Temp
= MenuOption
->Row
+ MenuOption
->Skip
+ DistanceValue
- 1;
3653 if ((MenuOption
->Row
+ MenuOption
->Skip
== BottomRow
+ 1) &&
3654 (NextMenuOption
->ThisTag
->Operand
== EFI_IFR_DATE_OP
||
3655 NextMenuOption
->ThisTag
->Operand
== EFI_IFR_TIME_OP
)
3661 // If we are going to scroll, update TopOfScreen
3663 if (Temp
> BottomRow
) {
3666 // Is the current top of screen a zero-advance op-code?
3667 // If so, keep moving forward till we hit a >0 advance op-code
3669 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3672 // If bottom op-code is more than one line or top op-code is more than one line
3674 if ((DistanceValue
> 1) || (MenuOption
->Skip
> 1)) {
3676 // Is the bottom op-code greater than or equal in size to the top op-code?
3678 if ((Temp
- BottomRow
) >= (SavedMenuOption
->Skip
- OldSkipValue
)) {
3680 // Skip the top op-code
3682 TopOfScreen
= TopOfScreen
->ForwardLink
;
3683 Difference
= (Temp
- BottomRow
) - (SavedMenuOption
->Skip
- OldSkipValue
);
3685 OldSkipValue
= Difference
;
3687 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3690 // If we have a remainder, skip that many more op-codes until we drain the remainder
3692 while (Difference
>= (INTN
) SavedMenuOption
->Skip
) {
3694 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3696 Difference
= Difference
- (INTN
) SavedMenuOption
->Skip
;
3697 TopOfScreen
= TopOfScreen
->ForwardLink
;
3698 SavedMenuOption
= MENU_OPTION_FROM_LINK (TopOfScreen
);
3701 // Since we will act on this op-code in the next routine, and increment the
3702 // SkipValue, set the skips to one less than what is required.
3704 SkipValue
= Difference
- 1;
3708 // Since we will act on this op-code in the next routine, and increment the
3709 // SkipValue, set the skips to one less than what is required.
3711 SkipValue
= OldSkipValue
+ (Temp
- BottomRow
) - 1;
3714 if ((OldSkipValue
+ 1) == (INTN
) SavedMenuOption
->Skip
) {
3715 TopOfScreen
= TopOfScreen
->ForwardLink
;
3718 SkipValue
= OldSkipValue
;
3722 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3723 // Let's set a skip flag to smoothly scroll the top of the screen.
3725 if (SavedMenuOption
->Skip
> 1) {
3726 if (SavedMenuOption
== NextMenuOption
) {
3731 } else if (SavedMenuOption
->Skip
== 1) {
3735 TopOfScreen
= TopOfScreen
->ForwardLink
;
3737 } while (SavedMenuOption
->Skip
== 0);
3740 OldSkipValue
= SkipValue
;
3741 } else if (!IsSelectable (NextMenuOption
)) {
3743 // Continue to go down until scroll to next page or the selectable option is found.
3745 ScreenOperation
= UiDown
;
3746 ControlFlag
= CfScreenOperation
;
3749 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3751 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3755 // Scroll to the first page.
3757 if (TopOfScreen
!= gMenuOption
.ForwardLink
) {
3758 TopOfScreen
= gMenuOption
.ForwardLink
;
3762 MenuOption
= MENU_OPTION_FROM_LINK (SavedListEntry
);
3765 NewPos
= gMenuOption
.ForwardLink
;
3766 MoveToNextStatement (Selection
, FALSE
, &NewPos
, BottomRow
- TopRow
);
3770 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3772 AdjustDateAndTimePosition (TRUE
, &TopOfScreen
);
3773 AdjustDateAndTimePosition (TRUE
, &NewPos
);
3777 ControlFlag
= CfCheckSelection
;
3779 Status
= EFI_SUCCESS
;
3781 // Discard changes. After it, no NV flag is showed.
3783 if ((HotKey
->Action
& BROWSER_ACTION_DISCARD
) == BROWSER_ACTION_DISCARD
) {
3784 Status
= DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3785 if (!EFI_ERROR (Status
)) {
3786 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3787 Selection
->Statement
= NULL
;
3788 gResetRequired
= FALSE
;
3791 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDiscardFailed
, gPressEnter
, gEmptyString
);
3792 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3794 // Still show current page.
3796 Selection
->Action
= UI_ACTION_NONE
;
3804 // Reterieve default setting. After it. NV flag will be showed.
3806 if ((HotKey
->Action
& BROWSER_ACTION_DEFAULT
) == BROWSER_ACTION_DEFAULT
) {
3807 Status
= ExtractDefault (Selection
->FormSet
, Selection
->Form
, HotKey
->DefaultId
, gBrowserSettingScope
, GetDefaultForAll
, NULL
);
3808 if (!EFI_ERROR (Status
)) {
3809 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3810 Selection
->Statement
= NULL
;
3811 gResetRequired
= TRUE
;
3814 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gDefaultFailed
, gPressEnter
, gEmptyString
);
3815 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3817 // Still show current page.
3819 Selection
->Action
= UI_ACTION_NONE
;
3827 // Save changes. After it, no NV flag is showed.
3829 if ((HotKey
->Action
& BROWSER_ACTION_SUBMIT
) == BROWSER_ACTION_SUBMIT
) {
3830 Status
= SubmitForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3831 if (!EFI_ERROR (Status
)) {
3832 ASSERT(MenuOption
!= NULL
);
3833 UpdateStatusBar (Selection
, INPUT_ERROR
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3834 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, MenuOption
->ThisTag
->QuestionFlags
, FALSE
);
3837 CreateDialog (4, TRUE
, 0, NULL
, &Key
, HotKey
->HelpString
, gSaveFailed
, gPressEnter
, gEmptyString
);
3838 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
3840 // Still show current page.
3842 Selection
->Action
= UI_ACTION_NONE
;
3850 // Set Reset required Flag
3852 if ((HotKey
->Action
& BROWSER_ACTION_RESET
) == BROWSER_ACTION_RESET
) {
3853 gResetRequired
= TRUE
;
3859 if ((HotKey
->Action
& BROWSER_ACTION_EXIT
) == BROWSER_ACTION_EXIT
) {
3861 // Form Exit without saving, Similar to ESC Key.
3862 // FormSet Exit without saving, Exit SendForm.
3863 // System Exit without saving, CallExitHandler and Exit SendForm.
3865 DiscardForm (Selection
->FormSet
, Selection
->Form
, gBrowserSettingScope
);
3866 if (gBrowserSettingScope
== FormLevel
) {
3867 ControlFlag
= CfUiReset
;
3868 } else if (gBrowserSettingScope
== FormSetLevel
) {
3869 Selection
->Action
= UI_ACTION_EXIT
;
3870 } else if (gBrowserSettingScope
== SystemLevel
) {
3871 if (ExitHandlerFunction
!= NULL
) {
3872 ExitHandlerFunction ();
3874 Selection
->Action
= UI_ACTION_EXIT
;
3876 Selection
->Statement
= NULL
;
3881 ControlFlag
= CfCheckSelection
;
3883 // Reset to default value for all forms in the whole system.
3885 Status
= ExtractDefault (Selection
->FormSet
, NULL
, DefaultId
, FormSetLevel
, GetDefaultForAll
, NULL
);
3887 if (!EFI_ERROR (Status
)) {
3888 Selection
->Action
= UI_ACTION_REFRESH_FORM
;
3889 Selection
->Statement
= NULL
;
3890 gResetRequired
= TRUE
;
3894 case CfUiNoOperation
:
3895 ControlFlag
= CfCheckSelection
;
3899 UiFreeRefreshList ();
3901 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
3902 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, 0, Row
+ 4);
3903 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
3904 gST
->ConOut
->OutputString (gST
->ConOut
, L
"\n");
3905 if (HelpString
!= NULL
) {
3906 FreePool (HelpString
);
3908 if (HelpHeaderString
!= NULL
) {
3909 FreePool (HelpHeaderString
);
3911 if (HelpBottomString
!= NULL
) {
3912 FreePool (HelpBottomString
);