2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2007, Intel Corporation
5 All rights reserved. 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.
20 Get string or password input from user.
22 @param MenuOption Pointer to the current input menu.
23 @param Prompt The prompt string shown on popup window.
24 @param StringPtr Destination for use input string.
26 @retval EFI_SUCCESS If string input is read successfully
27 @retval EFI_DEVICE_ERROR If operation fails
32 IN UI_MENU_OPTION
*MenuOption
,
44 CHAR16
*BufferedString
;
49 UINTN DimensionsWidth
;
50 UINTN DimensionsHeight
;
51 BOOLEAN CursorVisible
;
54 FORM_BROWSER_STATEMENT
*Question
;
57 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
58 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
60 NullCharacter
= CHAR_NULL
;
61 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
65 Question
= MenuOption
->ThisTag
;
66 Minimum
= (UINTN
) Question
->Minimum
;
67 Maximum
= (UINTN
) Question
->Maximum
;
69 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
75 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
78 if (ScreenSize
< (Maximum
+ 1)) {
79 ScreenSize
= Maximum
+ 1;
82 if ((ScreenSize
+ 2) > DimensionsWidth
) {
83 ScreenSize
= DimensionsWidth
- 2;
86 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
87 ASSERT (BufferedString
);
89 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
90 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
93 // Display prompt for string
95 CreatePopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
97 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
99 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
100 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
103 Status
= WaitForKeyStroke (&Key
);
104 ASSERT_EFI_ERROR (Status
);
106 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
107 switch (Key
.UnicodeChar
) {
109 switch (Key
.ScanCode
) {
117 FreePool (TempString
);
118 FreePool (BufferedString
);
119 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
120 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
121 return EFI_DEVICE_ERROR
;
129 case CHAR_CARRIAGE_RETURN
:
130 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
132 FreePool (TempString
);
133 FreePool (BufferedString
);
134 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
135 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
139 // Simply create a popup to tell the user that they had typed in too few characters.
140 // To save code space, we can then treat this as an error and return back to the menu.
143 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
144 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
146 FreePool (TempString
);
147 FreePool (BufferedString
);
148 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
149 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
150 return EFI_DEVICE_ERROR
;
156 if (StringPtr
[0] != CHAR_NULL
) {
157 for (Index
= 0; StringPtr
[Index
] != CHAR_NULL
; Index
++) {
158 TempString
[Index
] = StringPtr
[Index
];
161 // Effectively truncate string by 1 character
163 TempString
[Index
- 1] = CHAR_NULL
;
164 StrCpy (StringPtr
, TempString
);
169 // If it is the beginning of the string, don't worry about checking maximum limits
171 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
172 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
173 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
174 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
175 KeyPad
[0] = Key
.UnicodeChar
;
176 KeyPad
[1] = CHAR_NULL
;
177 StrCat (StringPtr
, KeyPad
);
178 StrCat (TempString
, KeyPad
);
182 // If the width of the input string is now larger than the screen, we nee to
183 // adjust the index to start printing portions of the string
185 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
186 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
188 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
189 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
195 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
198 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
199 BufferedString
[Count
] = StringPtr
[Index
];
207 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
212 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
213 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
220 This routine reads a numeric value from the user input.
222 @param Selection Pointer to current selection.
223 @param MenuOption Pointer to the current input menu.
225 @retval EFI_SUCCESS If numerical input is read successfully
226 @retval EFI_DEVICE_ERROR If operation fails
231 IN UI_MENU_SELECTION
*Selection
,
232 IN UI_MENU_OPTION
*MenuOption
238 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
239 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
240 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
254 EFI_HII_VALUE
*QuestionValue
;
255 FORM_BROWSER_FORM
*Form
;
256 FORM_BROWSER_FORMSET
*FormSet
;
257 FORM_BROWSER_STATEMENT
*Question
;
259 Column
= MenuOption
->OptCol
;
260 Row
= MenuOption
->Row
;
261 PreviousNumber
[0] = 0;
266 FormSet
= Selection
->FormSet
;
267 Form
= Selection
->Form
;
268 Question
= MenuOption
->ThisTag
;
269 QuestionValue
= &Question
->HiiValue
;
270 Step
= Question
->Step
;
271 Minimum
= Question
->Minimum
;
272 Maximum
= Question
->Maximum
;
274 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
281 // Prepare Value to be edit
285 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
289 switch (MenuOption
->Sequence
) {
293 EditValue
= QuestionValue
->Value
.date
.Month
;
299 EditValue
= QuestionValue
->Value
.date
.Day
;
305 EditValue
= QuestionValue
->Value
.date
.Year
;
311 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
315 switch (MenuOption
->Sequence
) {
319 EditValue
= QuestionValue
->Value
.time
.Hour
;
325 EditValue
= QuestionValue
->Value
.time
.Minute
;
331 EditValue
= QuestionValue
->Value
.time
.Second
;
341 EraseLen
= gOptionBlockWidth
;
342 EditValue
= QuestionValue
->Value
.u64
;
344 Maximum
= (UINT64
) -1;
354 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
355 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
363 InputWidth
= Question
->StorageWidth
* 2;
365 switch (Question
->StorageWidth
) {
388 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
389 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
390 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
391 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
392 InputText
[InputWidth
+ 2] = L
'\0';
394 PrintAt (Column
, Row
, InputText
);
399 // First time we enter this handler, we need to check to see if
400 // we were passed an increment or decrement directive
403 Key
.UnicodeChar
= CHAR_NULL
;
404 if (gDirection
!= 0) {
405 Key
.ScanCode
= gDirection
;
410 Status
= WaitForKeyStroke (&Key
);
413 switch (Key
.UnicodeChar
) {
417 if (Key
.UnicodeChar
== '+') {
418 Key
.ScanCode
= SCAN_RIGHT
;
420 Key
.ScanCode
= SCAN_LEFT
;
422 Key
.UnicodeChar
= CHAR_NULL
;
426 switch (Key
.ScanCode
) {
431 // By setting this value, we will return back to the caller.
432 // We need to do this since an auto-refresh will destroy the adjustment
433 // based on what the real-time-clock is showing. So we always commit
434 // upon changing the value.
436 gDirection
= SCAN_DOWN
;
440 if (Key
.ScanCode
== SCAN_LEFT
) {
441 if (EditValue
> Step
) {
442 EditValue
= EditValue
- Step
;
446 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
447 EditValue
= EditValue
+ Step
;
448 if (EditValue
> Maximum
) {
453 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
454 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
455 if (MenuOption
->Sequence
== 2) {
459 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", EditValue
);
464 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
467 if (MenuOption
->Sequence
== 0) {
468 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
469 } else if (MenuOption
->Sequence
== 1) {
470 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
472 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
473 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
475 if (MenuOption
->Sequence
== 0) {
476 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
477 } else if (MenuOption
->Sequence
== 1) {
478 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
481 QuestionValue
->Value
.u64
= EditValue
;
482 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
485 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
486 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
487 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
489 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
491 if (MenuOption
->Sequence
== 0) {
492 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
493 Column
= MenuOption
->OptCol
+ 1;
496 PrintStringAt (Column
, Row
, FormattedNumber
);
498 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
499 PrintChar (RIGHT_NUMERIC_DELIMITER
);
503 goto EnterCarriageReturn
;
508 goto EnterCarriageReturn
;
511 return EFI_DEVICE_ERROR
;
521 case CHAR_CARRIAGE_RETURN
:
523 // Store Edit value back to Question
525 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
526 switch (MenuOption
->Sequence
) {
528 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
532 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
536 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
542 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
543 switch (MenuOption
->Sequence
) {
545 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
549 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
553 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
563 QuestionValue
->Value
.u64
= EditValue
;
567 // Check to see if the Value is something reasonable against consistency limitations.
568 // If not, let's kick the error specified.
570 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
571 if (EFI_ERROR (Status
)) {
573 // Input value is not valid, restore Question Value
575 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
577 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
578 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
580 // NV flag is unnecessary for RTC type of Date/Time
582 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
595 // Remove a character
597 EditValue
= PreviousNumber
[Count
- 1];
598 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
601 PrintAt (Column
, Row
, L
" ");
608 if (!IsHexDigit (&Digital
, Key
.UnicodeChar
)) {
609 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
613 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
614 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
620 // If Count exceed input width, there is no way more is valid
622 if (Count
>= InputWidth
) {
626 // Someone typed something valid!
630 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
632 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
638 EditValue
= Key
.UnicodeChar
- L
'0';
642 if (EditValue
> Maximum
) {
643 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
644 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
645 EditValue
= PreviousNumber
[Count
];
648 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
652 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
653 PreviousNumber
[Count
] = EditValue
;
655 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
666 Get selection for OneOf and OrderedList (Left/Right will be ignored).
668 @param Selection Pointer to current selection.
669 @param MenuOption Pointer to the current input menu.
671 @retval EFI_SUCCESS If Option input is processed successfully
672 @retval EFI_DEVICE_ERROR If operation fails
676 GetSelectionInputPopUp (
677 IN UI_MENU_SELECTION
*Selection
,
678 IN UI_MENU_OPTION
*MenuOption
685 CHAR16
*TempStringPtr
;
687 UINTN TopOptionIndex
;
688 UINTN HighlightOptionIndex
;
693 UINTN PopUpMenuLines
;
694 UINTN MenuLinesInView
;
697 INT32 SavedAttribute
;
698 BOOLEAN ShowDownArrow
;
700 UINTN DimensionsWidth
;
704 EFI_HII_VALUE HiiValue
;
705 EFI_HII_VALUE
*HiiValueArray
;
707 QUESTION_OPTION
*OneOfOption
;
708 QUESTION_OPTION
*CurrentOption
;
709 FORM_BROWSER_STATEMENT
*Question
;
711 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
714 CurrentOption
= NULL
;
715 ShowDownArrow
= FALSE
;
718 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
721 Question
= MenuOption
->ThisTag
;
722 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
723 ValueArray
= Question
->BufferValue
;
730 // Calculate Option count
733 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
734 if (ValueArray
[Index
] == 0) {
742 Link
= GetFirstNode (&Question
->OptionListHead
);
743 while (!IsNull (&Question
->OptionListHead
, Link
)) {
744 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
748 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
753 // Prepare HiiValue array
755 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
756 ASSERT (HiiValueArray
!= NULL
);
757 Link
= GetFirstNode (&Question
->OptionListHead
);
758 for (Index
= 0; Index
< OptionCount
; Index
++) {
760 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
761 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
763 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
764 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
765 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
770 // Move Suppressed Option to list tail
773 for (Index
= 0; Index
< OptionCount
; Index
++) {
774 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
775 if (OneOfOption
== NULL
) {
776 return EFI_NOT_FOUND
;
779 RemoveEntryList (&OneOfOption
->Link
);
781 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
782 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
784 // This option is suppressed, insert to tail
786 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
791 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
798 // Get the number of one of options present and its size
801 HighlightOptionIndex
= 0;
802 Link
= GetFirstNode (&Question
->OptionListHead
);
803 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
804 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
806 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
807 if (StrLen (StringPtr
) > PopUpWidth
) {
808 PopUpWidth
= StrLen (StringPtr
);
810 FreePool (StringPtr
);
812 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
814 // Find current selected Option for OneOf
816 HighlightOptionIndex
= Index
;
819 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
823 // Perform popup menu initialization.
825 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
827 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
828 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
830 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
831 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
834 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
835 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
836 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
837 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
839 MenuLinesInView
= Bottom
- Top
- 1;
840 if (MenuLinesInView
>= PopUpMenuLines
) {
841 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
842 Bottom
= Top
+ PopUpMenuLines
+ 1;
844 ShowDownArrow
= TRUE
;
847 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
848 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
855 // Clear that portion of the screen
857 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
860 // Draw "One of" pop-up menu
862 Character
= BOXDRAW_DOWN_RIGHT
;
863 PrintCharAt (Start
, Top
, Character
);
864 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
865 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
866 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
868 Character
= BOXDRAW_HORIZONTAL
;
871 PrintChar (Character
);
874 Character
= BOXDRAW_DOWN_LEFT
;
875 PrintChar (Character
);
876 Character
= BOXDRAW_VERTICAL
;
877 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
878 PrintCharAt (Start
, Index
, Character
);
879 PrintCharAt (End
- 1, Index
, Character
);
883 // Move to top Option
885 Link
= GetFirstNode (&Question
->OptionListHead
);
886 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
887 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
891 // Display the One of options
894 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
895 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
896 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
898 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
900 // If the string occupies multiple lines, truncate it to fit in one line,
901 // and append a "..." for indication.
903 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
904 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
905 ASSERT ( TempStringPtr
!= NULL
);
906 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
907 FreePool (StringPtr
);
908 StringPtr
= TempStringPtr
;
909 StrCat (StringPtr
, L
"...");
912 if (Index
== HighlightOptionIndex
) {
914 // Highlight the selected one
916 CurrentOption
= OneOfOption
;
918 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
919 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
920 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
922 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
923 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
927 FreePool (StringPtr
);
930 Character
= BOXDRAW_UP_RIGHT
;
931 PrintCharAt (Start
, Bottom
, Character
);
932 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
933 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
934 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
936 Character
= BOXDRAW_HORIZONTAL
;
939 PrintChar (Character
);
942 Character
= BOXDRAW_UP_LEFT
;
943 PrintChar (Character
);
946 // Get User selection
948 Key
.UnicodeChar
= CHAR_NULL
;
949 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
950 Key
.ScanCode
= gDirection
;
955 Status
= WaitForKeyStroke (&Key
);
958 switch (Key
.UnicodeChar
) {
961 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
963 // Highlight reaches the top of the popup window, scroll one menu item.
966 ShowDownArrow
= TRUE
;
969 if (TopOptionIndex
== 0) {
973 if (HighlightOptionIndex
> 0) {
974 HighlightOptionIndex
--;
976 ASSERT (CurrentOption
!= NULL
);
977 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
984 // If an ordered list op-code, we will allow for a popup of +/- keys
985 // to create an ordered list of items
988 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
989 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
991 // Highlight reaches the bottom of the popup window, scroll one menu item.
997 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
998 ShowDownArrow
= FALSE
;
1001 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1002 HighlightOptionIndex
++;
1004 ASSERT (CurrentOption
!= NULL
);
1005 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1011 switch (Key
.ScanCode
) {
1014 if (Key
.ScanCode
== SCAN_UP
) {
1015 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1017 // Highlight reaches the top of the popup window, scroll one menu item.
1020 ShowDownArrow
= TRUE
;
1023 if (TopOptionIndex
== 0) {
1024 ShowUpArrow
= FALSE
;
1027 if (HighlightOptionIndex
> 0) {
1028 HighlightOptionIndex
--;
1031 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1032 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1034 // Highlight reaches the bottom of the popup window, scroll one menu item.
1040 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1041 ShowDownArrow
= FALSE
;
1044 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1045 HighlightOptionIndex
++;
1051 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1054 // Restore link list order for orderedlist
1057 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1058 HiiValue
.Value
.u64
= 0;
1059 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1060 HiiValue
.Value
.u8
= ValueArray
[Index
];
1061 if (HiiValue
.Value
.u8
!= 0) {
1065 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1066 if (OneOfOption
== NULL
) {
1067 return EFI_NOT_FOUND
;
1070 RemoveEntryList (&OneOfOption
->Link
);
1071 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1075 FreePool (HiiValueArray
);
1076 return EFI_DEVICE_ERROR
;
1084 case CHAR_CARRIAGE_RETURN
:
1086 // return the current selection
1090 Link
= GetFirstNode (&Question
->OptionListHead
);
1091 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1092 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1094 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1097 if (Index
> Question
->MaxContainers
) {
1101 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1104 ASSERT (CurrentOption
!= NULL
);
1105 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1108 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1109 FreePool (HiiValueArray
);
1111 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1112 if (EFI_ERROR (Status
)) {
1114 // Input value is not valid, restore Question Value
1116 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1118 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1119 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1132 Wait for a key to be pressed by user.
1134 @param Key The key which is pressed by user.
1136 @retval EFI_SUCCESS The function always completed successfully.
1141 OUT EFI_INPUT_KEY
*Key
1147 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1148 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1149 } while (EFI_ERROR(Status
));