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 // Validate input value with Minimum value.
732 if (EditValue
< Minimum
) {
733 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
736 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
740 // Store Edit value back to Question
742 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
743 switch (MenuOption
->Sequence
) {
745 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
749 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
753 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
759 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
760 switch (MenuOption
->Sequence
) {
762 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
766 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
770 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
780 QuestionValue
->Value
.u64
= EditValue
;
784 // Adjust the value to the correct one.
785 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
786 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
788 if (Question
->Operand
== EFI_IFR_DATE_OP
&&
789 (MenuOption
->Sequence
== 0 || MenuOption
->Sequence
== 2)) {
790 AdjustQuestionValue (Question
, (UINT8
)MenuOption
->Sequence
);
794 // Check to see if the Value is something reasonable against consistency limitations.
795 // If not, let's kick the error specified.
797 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
798 if (EFI_ERROR (Status
)) {
800 // Input value is not valid, restore Question Value
802 GetQuestionValue (FormSet
, Form
, Question
, GetSetValueWithEditBuffer
);
804 SetQuestionValue (FormSet
, Form
, Question
, GetSetValueWithEditBuffer
);
805 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
807 // NV flag is unnecessary for RTC type of Date/Time
809 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
822 // Remove a character
824 EditValue
= PreviousNumber
[Count
- 1];
825 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
828 PrintAt (Column
, Row
, L
" ");
835 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
836 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
837 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
838 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
839 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
840 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
842 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
846 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
847 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
853 // If Count exceed input width, there is no way more is valid
855 if (Count
>= InputWidth
) {
859 // Someone typed something valid!
863 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
865 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
871 EditValue
= Key
.UnicodeChar
- L
'0';
875 if (EditValue
> Maximum
) {
876 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
877 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
878 EditValue
= PreviousNumber
[Count
];
881 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
885 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
886 PreviousNumber
[Count
] = EditValue
;
888 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
899 Get selection for OneOf and OrderedList (Left/Right will be ignored).
901 @param Selection Pointer to current selection.
902 @param MenuOption Pointer to the current input menu.
904 @retval EFI_SUCCESS If Option input is processed successfully
905 @retval EFI_DEVICE_ERROR If operation fails
909 GetSelectionInputPopUp (
910 IN UI_MENU_SELECTION
*Selection
,
911 IN UI_MENU_OPTION
*MenuOption
918 CHAR16
*TempStringPtr
;
920 UINTN TopOptionIndex
;
921 UINTN HighlightOptionIndex
;
926 UINTN PopUpMenuLines
;
927 UINTN MenuLinesInView
;
930 INT32 SavedAttribute
;
931 BOOLEAN ShowDownArrow
;
933 UINTN DimensionsWidth
;
938 EFI_HII_VALUE HiiValue
;
939 EFI_HII_VALUE
*HiiValueArray
;
941 QUESTION_OPTION
*OneOfOption
;
942 QUESTION_OPTION
*CurrentOption
;
943 FORM_BROWSER_STATEMENT
*Question
;
946 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
950 CurrentOption
= NULL
;
951 ShowDownArrow
= FALSE
;
954 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
957 Question
= MenuOption
->ThisTag
;
958 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
959 ValueArray
= Question
->BufferValue
;
960 ValueType
= Question
->ValueType
;
967 // Calculate Option count
970 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
971 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
979 Link
= GetFirstNode (&Question
->OptionListHead
);
980 while (!IsNull (&Question
->OptionListHead
, Link
)) {
981 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
985 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
990 // Move valid Option to list head.
995 // Prepare HiiValue array
997 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
998 ASSERT (HiiValueArray
!= NULL
);
999 for (Index
= 0; Index
< OptionCount
; Index
++) {
1000 HiiValueArray
[Index
].Type
= ValueType
;
1001 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1004 for (Index
= 0; Index
< OptionCount
; Index
++) {
1005 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
1006 if (OneOfOption
== NULL
) {
1007 return EFI_NOT_FOUND
;
1010 RemoveEntryList (&OneOfOption
->Link
);
1015 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1020 FreePool (HiiValueArray
);
1022 Link
= GetFirstNode (&Question
->OptionListHead
);
1023 for (Index
= 0; Index
< OptionCount
; Index
++) {
1024 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1025 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1026 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
1027 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) > ExpressFalse
) {
1036 // Get the number of one of options present and its size
1039 HighlightOptionIndex
= 0;
1040 Link
= GetFirstNode (&Question
->OptionListHead
);
1041 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
1042 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1043 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1045 if (!OrderedList
&& (OneOfOption
->SuppressExpression
!= NULL
) &&
1046 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) > ExpressFalse
) {
1051 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
1052 if (StrLen (StringPtr
) > PopUpWidth
) {
1053 PopUpWidth
= StrLen (StringPtr
);
1055 FreePool (StringPtr
);
1057 if (!OrderedList
&& (CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, &Result
, NULL
) == EFI_SUCCESS
) && (Result
== 0)) {
1059 // Find current selected Option for OneOf
1061 HighlightOptionIndex
= Index
;
1066 // Perform popup menu initialization.
1068 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
1070 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
1071 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1073 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
1074 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
1077 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
1078 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
1079 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
1080 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- gFooterHeight
- 1;
1082 MenuLinesInView
= Bottom
- Top
- 1;
1083 if (MenuLinesInView
>= PopUpMenuLines
) {
1084 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
1085 Bottom
= Top
+ PopUpMenuLines
+ 1;
1087 ShowDownArrow
= TRUE
;
1090 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
1091 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
1098 // Clear that portion of the screen
1100 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
1103 // Draw "One of" pop-up menu
1105 Character
= BOXDRAW_DOWN_RIGHT
;
1106 PrintCharAt (Start
, Top
, Character
);
1107 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1108 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1109 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
1111 Character
= BOXDRAW_HORIZONTAL
;
1114 PrintChar (Character
);
1117 Character
= BOXDRAW_DOWN_LEFT
;
1118 PrintChar (Character
);
1119 Character
= BOXDRAW_VERTICAL
;
1120 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
1121 PrintCharAt (Start
, Index
, Character
);
1122 PrintCharAt (End
- 1, Index
, Character
);
1126 // Move to top Option
1128 Link
= GetFirstNode (&Question
->OptionListHead
);
1129 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
1130 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1132 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1133 if (!OrderedList
&& (OneOfOption
->SuppressExpression
!= NULL
) &&
1134 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) > ExpressFalse
) {
1141 // Display the One of options
1144 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
1145 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1146 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1148 if (!OrderedList
&& (OneOfOption
->SuppressExpression
!= NULL
) &&
1149 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) > ExpressFalse
) {
1154 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
1155 ASSERT (StringPtr
!= NULL
);
1157 // If the string occupies multiple lines, truncate it to fit in one line,
1158 // and append a "..." for indication.
1160 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
1161 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
1162 ASSERT ( TempStringPtr
!= NULL
);
1163 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
1164 FreePool (StringPtr
);
1165 StringPtr
= TempStringPtr
;
1166 StrCat (StringPtr
, L
"...");
1169 if (Index
== HighlightOptionIndex
) {
1171 // Highlight the selected one
1173 CurrentOption
= OneOfOption
;
1175 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
1176 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1177 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1179 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1180 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1184 FreePool (StringPtr
);
1187 Character
= BOXDRAW_UP_RIGHT
;
1188 PrintCharAt (Start
, Bottom
, Character
);
1189 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1190 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1191 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
1193 Character
= BOXDRAW_HORIZONTAL
;
1196 PrintChar (Character
);
1199 Character
= BOXDRAW_UP_LEFT
;
1200 PrintChar (Character
);
1203 // Get User selection
1205 Key
.UnicodeChar
= CHAR_NULL
;
1206 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
1207 Key
.ScanCode
= gDirection
;
1212 Status
= WaitForKeyStroke (&Key
);
1215 switch (Key
.UnicodeChar
) {
1218 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1220 // Highlight reaches the top of the popup window, scroll one menu item.
1223 ShowDownArrow
= TRUE
;
1226 if (TopOptionIndex
== 0) {
1227 ShowUpArrow
= FALSE
;
1230 if (HighlightOptionIndex
> 0) {
1231 HighlightOptionIndex
--;
1233 ASSERT (CurrentOption
!= NULL
);
1234 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
1241 // If an ordered list op-code, we will allow for a popup of +/- keys
1242 // to create an ordered list of items
1245 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1246 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1248 // Highlight reaches the bottom of the popup window, scroll one menu item.
1254 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1255 ShowDownArrow
= FALSE
;
1258 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1259 HighlightOptionIndex
++;
1261 ASSERT (CurrentOption
!= NULL
);
1262 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1268 switch (Key
.ScanCode
) {
1271 if (Key
.ScanCode
== SCAN_UP
) {
1272 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1274 // Highlight reaches the top of the popup window, scroll one menu item.
1277 ShowDownArrow
= TRUE
;
1280 if (TopOptionIndex
== 0) {
1281 ShowUpArrow
= FALSE
;
1284 if (HighlightOptionIndex
> 0) {
1285 HighlightOptionIndex
--;
1288 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1289 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1291 // Highlight reaches the bottom of the popup window, scroll one menu item.
1297 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1298 ShowDownArrow
= FALSE
;
1301 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1302 HighlightOptionIndex
++;
1308 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1311 // Restore link list order for orderedlist
1314 HiiValue
.Type
= ValueType
;
1315 HiiValue
.Value
.u64
= 0;
1316 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1317 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1318 if (HiiValue
.Value
.u64
== 0) {
1322 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1323 if (OneOfOption
== NULL
) {
1324 return EFI_NOT_FOUND
;
1327 RemoveEntryList (&OneOfOption
->Link
);
1328 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1332 return EFI_DEVICE_ERROR
;
1340 case CHAR_CARRIAGE_RETURN
:
1342 // return the current selection
1346 Link
= GetFirstNode (&Question
->OptionListHead
);
1347 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1348 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1349 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1351 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
1352 EvaluateExpressionList(OneOfOption
->SuppressExpression
, FALSE
, NULL
, NULL
) != ExpressFalse
) {
1356 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1359 if (Index
> Question
->MaxContainers
) {
1364 ASSERT (CurrentOption
!= NULL
);
1365 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1368 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1370 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1371 if (EFI_ERROR (Status
)) {
1373 // Input value is not valid, restore Question Value
1375 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, GetSetValueWithEditBuffer
);
1377 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, GetSetValueWithEditBuffer
);
1378 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1391 Wait for a key to be pressed by user.
1393 @param Key The key which is pressed by user.
1395 @retval EFI_SUCCESS The function always completed successfully.
1400 OUT EFI_INPUT_KEY
*Key
1406 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1407 if (!EFI_ERROR (Status
)) {
1411 if (Status
!= EFI_NOT_READY
) {
1415 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);