2 Implementation for handling user input from the User Interfaces.
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.
19 Get string or password input from user.
21 @param MenuOption Pointer to the current input menu.
22 @param Prompt The prompt string shown on popup window.
23 @param StringPtr Old user input and destination for use input string.
25 @retval EFI_SUCCESS If string input is read successfully
26 @retval EFI_DEVICE_ERROR If operation fails
31 IN UI_MENU_OPTION
*MenuOption
,
33 IN OUT CHAR16
*StringPtr
43 CHAR16
*BufferedString
;
49 UINTN DimensionsWidth
;
50 UINTN DimensionsHeight
;
52 BOOLEAN CursorVisible
;
55 FORM_BROWSER_STATEMENT
*Question
;
58 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
59 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
61 NullCharacter
= CHAR_NULL
;
62 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
66 Question
= MenuOption
->ThisTag
;
67 Minimum
= (UINTN
) Question
->Minimum
;
68 Maximum
= (UINTN
) Question
->Maximum
;
70 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
76 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
79 if (ScreenSize
< (Maximum
+ 1)) {
80 ScreenSize
= Maximum
+ 1;
83 if ((ScreenSize
+ 2) > DimensionsWidth
) {
84 ScreenSize
= DimensionsWidth
- 2;
87 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
88 ASSERT (BufferedString
);
90 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
91 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
94 // Display prompt for string
96 CreateMultiStringPopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
98 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
100 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
101 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
103 CurrentCursor
= GetStringWidth (StringPtr
) / 2 - 1;
104 if (CurrentCursor
!= 0) {
106 // Show the string which has beed saved before.
108 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
109 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
111 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
112 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
118 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
121 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
122 BufferedString
[Count
] = StringPtr
[Index
];
130 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
133 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
134 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
138 Status
= WaitForKeyStroke (&Key
);
139 ASSERT_EFI_ERROR (Status
);
141 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
142 switch (Key
.UnicodeChar
) {
144 switch (Key
.ScanCode
) {
146 if (CurrentCursor
> 0) {
152 if (CurrentCursor
< (GetStringWidth (StringPtr
) / 2 - 1)) {
158 FreePool (TempString
);
159 FreePool (BufferedString
);
160 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
161 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
162 return EFI_DEVICE_ERROR
;
170 case CHAR_CARRIAGE_RETURN
:
171 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
173 FreePool (TempString
);
174 FreePool (BufferedString
);
175 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
176 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
180 // Simply create a popup to tell the user that they had typed in too few characters.
181 // To save code space, we can then treat this as an error and return back to the menu.
184 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
185 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
187 FreePool (TempString
);
188 FreePool (BufferedString
);
189 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
190 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
191 return EFI_DEVICE_ERROR
;
197 if (StringPtr
[0] != CHAR_NULL
&& CurrentCursor
!= 0) {
198 for (Index
= 0; Index
< CurrentCursor
- 1; Index
++) {
199 TempString
[Index
] = StringPtr
[Index
];
201 Count
= GetStringWidth (StringPtr
) / 2 - 1;
202 if (Count
>= CurrentCursor
) {
203 for (Index
= CurrentCursor
- 1, Index2
= CurrentCursor
; Index2
< Count
; Index
++, Index2
++) {
204 TempString
[Index
] = StringPtr
[Index2
];
206 TempString
[Index
] = CHAR_NULL
;
209 // Effectively truncate string by 1 character
211 StrCpy (StringPtr
, TempString
);
217 // If it is the beginning of the string, don't worry about checking maximum limits
219 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
220 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
222 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
223 KeyPad
[0] = Key
.UnicodeChar
;
224 KeyPad
[1] = CHAR_NULL
;
225 Count
= GetStringWidth (StringPtr
) / 2 - 1;
226 if (CurrentCursor
< Count
) {
227 for (Index
= 0; Index
< CurrentCursor
; Index
++) {
228 TempString
[Index
] = StringPtr
[Index
];
230 TempString
[Index
] = CHAR_NULL
;
231 StrCat (TempString
, KeyPad
);
232 StrCat (TempString
, StringPtr
+ CurrentCursor
);
233 StrCpy (StringPtr
, TempString
);
235 StrCat (StringPtr
, KeyPad
);
241 // If the width of the input string is now larger than the screen, we nee to
242 // adjust the index to start printing portions of the string
244 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
245 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
247 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
248 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
254 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
257 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
258 BufferedString
[Count
] = StringPtr
[Index
];
266 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
271 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
272 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ CurrentCursor
+ 1, Top
+ 3);
278 Adjust the value to the correct one. Rules follow the sample:
279 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
280 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
282 @param Question Pointer to current question.
283 @param Sequence The sequence of the field in the question.
286 AdjustQuestionValue (
287 IN FORM_BROWSER_STATEMENT
*Question
,
296 if (Question
->Operand
!= EFI_IFR_DATE_OP
) {
300 Month
= Question
->HiiValue
.Value
.date
.Month
;
301 Year
= Question
->HiiValue
.Value
.date
.Year
;
306 if ((Year
% 4) == 0 && ((Year
% 100) != 0 || (Year
% 400) == 0)) {
324 // Change the month area.
327 if (Question
->HiiValue
.Value
.date
.Day
> Maximum
) {
328 Question
->HiiValue
.Value
.date
.Day
= Maximum
;
333 // Change the Year area.
336 if (Question
->HiiValue
.Value
.date
.Day
> Maximum
) {
337 Question
->HiiValue
.Value
.date
.Day
= Minimum
;
343 This routine reads a numeric value from the user input.
345 @param Selection Pointer to current selection.
346 @param MenuOption Pointer to the current input menu.
348 @retval EFI_SUCCESS If numerical input is read successfully
349 @retval EFI_DEVICE_ERROR If operation fails
354 IN UI_MENU_SELECTION
*Selection
,
355 IN UI_MENU_OPTION
*MenuOption
361 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
362 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
363 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
377 EFI_HII_VALUE
*QuestionValue
;
378 FORM_BROWSER_FORM
*Form
;
379 FORM_BROWSER_FORMSET
*FormSet
;
380 FORM_BROWSER_STATEMENT
*Question
;
382 Column
= MenuOption
->OptCol
;
383 Row
= MenuOption
->Row
;
384 PreviousNumber
[0] = 0;
389 FormSet
= Selection
->FormSet
;
390 Form
= Selection
->Form
;
391 Question
= MenuOption
->ThisTag
;
392 QuestionValue
= &Question
->HiiValue
;
393 Step
= Question
->Step
;
394 Minimum
= Question
->Minimum
;
395 Maximum
= Question
->Maximum
;
398 // Only two case, user can enter to this function: Enter and +/- case.
399 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
401 ManualInput
= (BOOLEAN
)(gDirection
== 0 ? TRUE
: FALSE
);
403 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
410 // Prepare Value to be edit
414 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
418 switch (MenuOption
->Sequence
) {
422 EditValue
= QuestionValue
->Value
.date
.Month
;
426 switch (QuestionValue
->Value
.date
.Month
) {
428 if ((QuestionValue
->Value
.date
.Year
% 4) == 0 &&
429 ((QuestionValue
->Value
.date
.Year
% 100) != 0 ||
430 (QuestionValue
->Value
.date
.Year
% 400) == 0)) {
448 EditValue
= QuestionValue
->Value
.date
.Day
;
454 EditValue
= QuestionValue
->Value
.date
.Year
;
460 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
464 switch (MenuOption
->Sequence
) {
468 EditValue
= QuestionValue
->Value
.time
.Hour
;
474 EditValue
= QuestionValue
->Value
.time
.Minute
;
480 EditValue
= QuestionValue
->Value
.time
.Second
;
490 EraseLen
= gOptionBlockWidth
;
491 EditValue
= QuestionValue
->Value
.u64
;
493 Maximum
= (UINT64
) -1;
497 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
498 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
505 // Enter from "Enter" input, clear the old word showing.
508 if (Question
->Operand
== EFI_IFR_NUMERIC_OP
) {
510 InputWidth
= Question
->StorageWidth
* 2;
512 switch (Question
->StorageWidth
) {
535 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
536 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
537 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
538 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
539 InputText
[InputWidth
+ 2] = L
'\0';
541 PrintAt (Column
, Row
, InputText
);
545 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
546 if (MenuOption
->Sequence
== 2) {
552 if (MenuOption
->Sequence
== 0) {
553 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
554 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
556 SetUnicodeMem (InputText
, InputWidth
, L
' ');
559 if (MenuOption
->Sequence
== 2) {
560 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
562 InputText
[InputWidth
+ 1] = DATE_SEPARATOR
;
564 InputText
[InputWidth
+ 2] = L
'\0';
566 PrintAt (Column
, Row
, InputText
);
567 if (MenuOption
->Sequence
== 0) {
572 if (Question
->Operand
== EFI_IFR_TIME_OP
) {
575 if (MenuOption
->Sequence
== 0) {
576 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
577 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
579 SetUnicodeMem (InputText
, InputWidth
, L
' ');
582 if (MenuOption
->Sequence
== 2) {
583 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
585 InputText
[InputWidth
+ 1] = TIME_SEPARATOR
;
587 InputText
[InputWidth
+ 2] = L
'\0';
589 PrintAt (Column
, Row
, InputText
);
590 if (MenuOption
->Sequence
== 0) {
597 // First time we enter this handler, we need to check to see if
598 // we were passed an increment or decrement directive
601 Key
.UnicodeChar
= CHAR_NULL
;
602 if (gDirection
!= 0) {
603 Key
.ScanCode
= gDirection
;
608 Status
= WaitForKeyStroke (&Key
);
611 switch (Key
.UnicodeChar
) {
615 if (Key
.UnicodeChar
== '+') {
616 Key
.ScanCode
= SCAN_RIGHT
;
618 Key
.ScanCode
= SCAN_LEFT
;
620 Key
.UnicodeChar
= CHAR_NULL
;
624 switch (Key
.ScanCode
) {
627 if (DateOrTime
&& !ManualInput
) {
629 // By setting this value, we will return back to the caller.
630 // We need to do this since an auto-refresh will destroy the adjustment
631 // based on what the real-time-clock is showing. So we always commit
632 // upon changing the value.
634 gDirection
= SCAN_DOWN
;
637 if ((Step
!= 0) && !ManualInput
) {
638 if (Key
.ScanCode
== SCAN_LEFT
) {
639 if (EditValue
>= Minimum
+ Step
) {
640 EditValue
= EditValue
- Step
;
641 } else if (EditValue
> Minimum
){
646 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
647 if (EditValue
+ Step
<= Maximum
) {
648 EditValue
= EditValue
+ Step
;
649 } else if (EditValue
< Maximum
) {
656 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
657 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
658 if (MenuOption
->Sequence
== 2) {
662 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", (UINT16
) EditValue
);
667 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
670 if (MenuOption
->Sequence
== 0) {
671 ASSERT (EraseLen
>= 2);
672 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
673 } else if (MenuOption
->Sequence
== 1) {
674 ASSERT (EraseLen
>= 1);
675 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
677 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
678 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
680 if (MenuOption
->Sequence
== 0) {
681 ASSERT (EraseLen
>= 2);
682 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
683 } else if (MenuOption
->Sequence
== 1) {
684 ASSERT (EraseLen
>= 1);
685 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
688 QuestionValue
->Value
.u64
= EditValue
;
689 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
692 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
693 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
694 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
696 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
698 if (MenuOption
->Sequence
== 0) {
699 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
700 Column
= MenuOption
->OptCol
+ 1;
703 PrintStringAt (Column
, Row
, FormattedNumber
);
705 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
706 PrintChar (RIGHT_NUMERIC_DELIMITER
);
710 goto EnterCarriageReturn
;
715 goto EnterCarriageReturn
;
718 return EFI_DEVICE_ERROR
;
728 case CHAR_CARRIAGE_RETURN
:
730 // Store Edit value back to Question
732 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
733 switch (MenuOption
->Sequence
) {
735 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
739 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
743 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
749 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
750 switch (MenuOption
->Sequence
) {
752 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
756 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
760 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
770 QuestionValue
->Value
.u64
= EditValue
;
774 // Adjust the value to the correct one.
775 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
776 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
778 if (Question
->Operand
== EFI_IFR_DATE_OP
&&
779 (MenuOption
->Sequence
== 0 || MenuOption
->Sequence
== 2)) {
780 AdjustQuestionValue (Question
, (UINT8
)MenuOption
->Sequence
);
784 // Check to see if the Value is something reasonable against consistency limitations.
785 // If not, let's kick the error specified.
787 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
788 if (EFI_ERROR (Status
)) {
790 // Input value is not valid, restore Question Value
792 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
794 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
795 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
797 // NV flag is unnecessary for RTC type of Date/Time
799 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
812 // Remove a character
814 EditValue
= PreviousNumber
[Count
- 1];
815 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
818 PrintAt (Column
, Row
, L
" ");
825 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
826 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
827 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
828 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
829 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
830 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
832 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
836 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
837 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
843 // If Count exceed input width, there is no way more is valid
845 if (Count
>= InputWidth
) {
849 // Someone typed something valid!
853 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
855 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
861 EditValue
= Key
.UnicodeChar
- L
'0';
865 if (EditValue
> Maximum
) {
866 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
867 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
868 EditValue
= PreviousNumber
[Count
];
871 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
875 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
876 PreviousNumber
[Count
] = EditValue
;
878 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
889 Get selection for OneOf and OrderedList (Left/Right will be ignored).
891 @param Selection Pointer to current selection.
892 @param MenuOption Pointer to the current input menu.
894 @retval EFI_SUCCESS If Option input is processed successfully
895 @retval EFI_DEVICE_ERROR If operation fails
899 GetSelectionInputPopUp (
900 IN UI_MENU_SELECTION
*Selection
,
901 IN UI_MENU_OPTION
*MenuOption
908 CHAR16
*TempStringPtr
;
910 UINTN TopOptionIndex
;
911 UINTN HighlightOptionIndex
;
916 UINTN PopUpMenuLines
;
917 UINTN MenuLinesInView
;
920 INT32 SavedAttribute
;
921 BOOLEAN ShowDownArrow
;
923 UINTN DimensionsWidth
;
928 EFI_HII_VALUE HiiValue
;
929 EFI_HII_VALUE
*HiiValueArray
;
931 QUESTION_OPTION
*OneOfOption
;
932 QUESTION_OPTION
*CurrentOption
;
933 FORM_BROWSER_STATEMENT
*Question
;
936 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
940 CurrentOption
= NULL
;
941 ShowDownArrow
= FALSE
;
944 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
947 Question
= MenuOption
->ThisTag
;
948 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
949 ValueArray
= Question
->BufferValue
;
950 ValueType
= Question
->ValueType
;
957 // Calculate Option count
960 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
961 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
969 Link
= GetFirstNode (&Question
->OptionListHead
);
970 while (!IsNull (&Question
->OptionListHead
, Link
)) {
971 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
975 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
980 // Prepare HiiValue array
982 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
983 ASSERT (HiiValueArray
!= NULL
);
984 Link
= GetFirstNode (&Question
->OptionListHead
);
985 for (Index
= 0; Index
< OptionCount
; Index
++) {
987 HiiValueArray
[Index
].Type
= ValueType
;
988 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
990 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
991 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
992 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
997 // Move Suppressed Option to list tail
1000 for (Index
= 0; Index
< OptionCount
; Index
++) {
1001 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
1002 if (OneOfOption
== NULL
) {
1003 return EFI_NOT_FOUND
;
1006 RemoveEntryList (&OneOfOption
->Link
);
1008 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
1009 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) != ExpressFalse
) {
1011 // This option is suppressed, insert to tail
1013 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1018 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1025 // Get the number of one of options present and its size
1028 HighlightOptionIndex
= 0;
1029 Link
= GetFirstNode (&Question
->OptionListHead
);
1030 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
1031 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1033 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
1034 if (StrLen (StringPtr
) > PopUpWidth
) {
1035 PopUpWidth
= StrLen (StringPtr
);
1037 FreePool (StringPtr
);
1039 if (!OrderedList
&& (CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, &Result
, NULL
) == EFI_SUCCESS
) && (Result
== 0)) {
1041 // Find current selected Option for OneOf
1043 HighlightOptionIndex
= Index
;
1046 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1050 // Perform popup menu initialization.
1052 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
1054 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
1055 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1057 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
1058 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
1061 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
1062 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
1063 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
1064 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- 1;
1066 MenuLinesInView
= Bottom
- Top
- 1;
1067 if (MenuLinesInView
>= PopUpMenuLines
) {
1068 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
1069 Bottom
= Top
+ PopUpMenuLines
+ 1;
1071 ShowDownArrow
= TRUE
;
1074 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
1075 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
1082 // Clear that portion of the screen
1084 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
1087 // Draw "One of" pop-up menu
1089 Character
= BOXDRAW_DOWN_RIGHT
;
1090 PrintCharAt (Start
, Top
, Character
);
1091 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1092 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1093 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
1095 Character
= BOXDRAW_HORIZONTAL
;
1098 PrintChar (Character
);
1101 Character
= BOXDRAW_DOWN_LEFT
;
1102 PrintChar (Character
);
1103 Character
= BOXDRAW_VERTICAL
;
1104 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
1105 PrintCharAt (Start
, Index
, Character
);
1106 PrintCharAt (End
- 1, Index
, Character
);
1110 // Move to top Option
1112 Link
= GetFirstNode (&Question
->OptionListHead
);
1113 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
1114 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1118 // Display the One of options
1121 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
1122 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1123 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1125 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
1126 ASSERT (StringPtr
!= NULL
);
1128 // If the string occupies multiple lines, truncate it to fit in one line,
1129 // and append a "..." for indication.
1131 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
1132 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
1133 ASSERT ( TempStringPtr
!= NULL
);
1134 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
1135 FreePool (StringPtr
);
1136 StringPtr
= TempStringPtr
;
1137 StrCat (StringPtr
, L
"...");
1140 if (Index
== HighlightOptionIndex
) {
1142 // Highlight the selected one
1144 CurrentOption
= OneOfOption
;
1146 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
1147 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1148 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1150 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1151 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1155 FreePool (StringPtr
);
1158 Character
= BOXDRAW_UP_RIGHT
;
1159 PrintCharAt (Start
, Bottom
, Character
);
1160 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1161 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1162 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
1164 Character
= BOXDRAW_HORIZONTAL
;
1167 PrintChar (Character
);
1170 Character
= BOXDRAW_UP_LEFT
;
1171 PrintChar (Character
);
1174 // Get User selection
1176 Key
.UnicodeChar
= CHAR_NULL
;
1177 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
1178 Key
.ScanCode
= gDirection
;
1183 Status
= WaitForKeyStroke (&Key
);
1186 switch (Key
.UnicodeChar
) {
1189 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1191 // Highlight reaches the top of the popup window, scroll one menu item.
1194 ShowDownArrow
= TRUE
;
1197 if (TopOptionIndex
== 0) {
1198 ShowUpArrow
= FALSE
;
1201 if (HighlightOptionIndex
> 0) {
1202 HighlightOptionIndex
--;
1204 ASSERT (CurrentOption
!= NULL
);
1205 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
1212 // If an ordered list op-code, we will allow for a popup of +/- keys
1213 // to create an ordered list of items
1216 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1217 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1219 // Highlight reaches the bottom of the popup window, scroll one menu item.
1225 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1226 ShowDownArrow
= FALSE
;
1229 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1230 HighlightOptionIndex
++;
1232 ASSERT (CurrentOption
!= NULL
);
1233 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1239 switch (Key
.ScanCode
) {
1242 if (Key
.ScanCode
== SCAN_UP
) {
1243 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1245 // Highlight reaches the top of the popup window, scroll one menu item.
1248 ShowDownArrow
= TRUE
;
1251 if (TopOptionIndex
== 0) {
1252 ShowUpArrow
= FALSE
;
1255 if (HighlightOptionIndex
> 0) {
1256 HighlightOptionIndex
--;
1259 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1260 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1262 // Highlight reaches the bottom of the popup window, scroll one menu item.
1268 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1269 ShowDownArrow
= FALSE
;
1272 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1273 HighlightOptionIndex
++;
1279 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1282 // Restore link list order for orderedlist
1285 HiiValue
.Type
= ValueType
;
1286 HiiValue
.Value
.u64
= 0;
1287 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1288 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1289 if (HiiValue
.Value
.u64
== 0) {
1293 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1294 if (OneOfOption
== NULL
) {
1295 return EFI_NOT_FOUND
;
1298 RemoveEntryList (&OneOfOption
->Link
);
1299 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1303 FreePool (HiiValueArray
);
1304 return EFI_DEVICE_ERROR
;
1312 case CHAR_CARRIAGE_RETURN
:
1314 // return the current selection
1318 Link
= GetFirstNode (&Question
->OptionListHead
);
1319 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1320 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1322 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1325 if (Index
> Question
->MaxContainers
) {
1329 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1332 ASSERT (CurrentOption
!= NULL
);
1333 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1336 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1337 FreePool (HiiValueArray
);
1339 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1340 if (EFI_ERROR (Status
)) {
1342 // Input value is not valid, restore Question Value
1344 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1346 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1347 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1360 Wait for a key to be pressed by user.
1362 @param Key The key which is pressed by user.
1364 @retval EFI_SUCCESS The function always completed successfully.
1369 OUT EFI_INPUT_KEY
*Key
1375 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1376 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1377 } while (EFI_ERROR(Status
));