2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2011, 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 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
,
43 CHAR16
*BufferedString
;
48 UINTN DimensionsWidth
;
49 UINTN DimensionsHeight
;
50 BOOLEAN CursorVisible
;
53 FORM_BROWSER_STATEMENT
*Question
;
56 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
57 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
59 NullCharacter
= CHAR_NULL
;
60 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
64 Question
= MenuOption
->ThisTag
;
65 Minimum
= (UINTN
) Question
->Minimum
;
66 Maximum
= (UINTN
) Question
->Maximum
;
68 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
74 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
77 if (ScreenSize
< (Maximum
+ 1)) {
78 ScreenSize
= Maximum
+ 1;
81 if ((ScreenSize
+ 2) > DimensionsWidth
) {
82 ScreenSize
= DimensionsWidth
- 2;
85 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
86 ASSERT (BufferedString
);
88 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
89 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
92 // Display prompt for string
94 CreateMultiStringPopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
96 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
98 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
99 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
102 Status
= WaitForKeyStroke (&Key
);
103 ASSERT_EFI_ERROR (Status
);
105 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
106 switch (Key
.UnicodeChar
) {
108 switch (Key
.ScanCode
) {
116 FreePool (TempString
);
117 FreePool (BufferedString
);
118 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
119 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
120 return EFI_DEVICE_ERROR
;
128 case CHAR_CARRIAGE_RETURN
:
129 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
131 FreePool (TempString
);
132 FreePool (BufferedString
);
133 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
134 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
138 // Simply create a popup to tell the user that they had typed in too few characters.
139 // To save code space, we can then treat this as an error and return back to the menu.
142 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
143 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
145 FreePool (TempString
);
146 FreePool (BufferedString
);
147 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
148 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
149 return EFI_DEVICE_ERROR
;
155 if (StringPtr
[0] != CHAR_NULL
) {
156 for (Index
= 0; StringPtr
[Index
] != CHAR_NULL
; Index
++) {
157 TempString
[Index
] = StringPtr
[Index
];
160 // Effectively truncate string by 1 character
162 TempString
[Index
- 1] = CHAR_NULL
;
163 StrCpy (StringPtr
, TempString
);
168 // If it is the beginning of the string, don't worry about checking maximum limits
170 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
171 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
172 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
173 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
174 KeyPad
[0] = Key
.UnicodeChar
;
175 KeyPad
[1] = CHAR_NULL
;
176 StrCat (StringPtr
, KeyPad
);
177 StrCat (TempString
, KeyPad
);
181 // If the width of the input string is now larger than the screen, we nee to
182 // adjust the index to start printing portions of the string
184 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
185 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
187 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
188 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
194 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
197 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
198 BufferedString
[Count
] = StringPtr
[Index
];
206 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
211 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
212 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
218 Adjust the value to the correct one. Rules follow the sample:
219 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
220 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
222 @param Question Pointer to current question.
223 @param Sequence The sequence of the field in the question.
226 AdjustQuestionValue (
227 IN FORM_BROWSER_STATEMENT
*Question
,
236 if (Question
->Operand
!= EFI_IFR_DATE_OP
) {
240 Month
= Question
->HiiValue
.Value
.date
.Month
;
241 Year
= Question
->HiiValue
.Value
.date
.Year
;
246 if ((Year
% 4) == 0 && ((Year
% 100) != 0 || (Year
% 400) == 0)) {
264 // Change the month area.
267 if (Question
->HiiValue
.Value
.date
.Day
> Maximum
) {
268 Question
->HiiValue
.Value
.date
.Day
= Maximum
;
273 // Change the Year area.
276 if (Question
->HiiValue
.Value
.date
.Day
> Maximum
) {
277 Question
->HiiValue
.Value
.date
.Day
= Minimum
;
283 This routine reads a numeric value from the user input.
285 @param Selection Pointer to current selection.
286 @param MenuOption Pointer to the current input menu.
288 @retval EFI_SUCCESS If numerical input is read successfully
289 @retval EFI_DEVICE_ERROR If operation fails
294 IN UI_MENU_SELECTION
*Selection
,
295 IN UI_MENU_OPTION
*MenuOption
301 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
302 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
303 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
317 EFI_HII_VALUE
*QuestionValue
;
318 FORM_BROWSER_FORM
*Form
;
319 FORM_BROWSER_FORMSET
*FormSet
;
320 FORM_BROWSER_STATEMENT
*Question
;
322 Column
= MenuOption
->OptCol
;
323 Row
= MenuOption
->Row
;
324 PreviousNumber
[0] = 0;
329 FormSet
= Selection
->FormSet
;
330 Form
= Selection
->Form
;
331 Question
= MenuOption
->ThisTag
;
332 QuestionValue
= &Question
->HiiValue
;
333 Step
= Question
->Step
;
334 Minimum
= Question
->Minimum
;
335 Maximum
= Question
->Maximum
;
338 // Only two case, user can enter to this function: Enter and +/- case.
339 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
341 ManualInput
= (BOOLEAN
)(gDirection
== 0 ? TRUE
: FALSE
);
343 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
350 // Prepare Value to be edit
354 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
358 switch (MenuOption
->Sequence
) {
362 EditValue
= QuestionValue
->Value
.date
.Month
;
366 switch (QuestionValue
->Value
.date
.Month
) {
368 if ((QuestionValue
->Value
.date
.Year
% 4) == 0 &&
369 ((QuestionValue
->Value
.date
.Year
% 100) != 0 ||
370 (QuestionValue
->Value
.date
.Year
% 400) == 0)) {
388 EditValue
= QuestionValue
->Value
.date
.Day
;
394 EditValue
= QuestionValue
->Value
.date
.Year
;
400 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
404 switch (MenuOption
->Sequence
) {
408 EditValue
= QuestionValue
->Value
.time
.Hour
;
414 EditValue
= QuestionValue
->Value
.time
.Minute
;
420 EditValue
= QuestionValue
->Value
.time
.Second
;
430 EraseLen
= gOptionBlockWidth
;
431 EditValue
= QuestionValue
->Value
.u64
;
433 Maximum
= (UINT64
) -1;
437 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
438 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
445 // Enter from "Enter" input, clear the old word showing.
448 if (Question
->Operand
== EFI_IFR_NUMERIC_OP
) {
450 InputWidth
= Question
->StorageWidth
* 2;
452 switch (Question
->StorageWidth
) {
475 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
476 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
477 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
478 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
479 InputText
[InputWidth
+ 2] = L
'\0';
481 PrintAt (Column
, Row
, InputText
);
485 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
486 if (MenuOption
->Sequence
== 2) {
492 if (MenuOption
->Sequence
== 0) {
493 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
494 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
496 SetUnicodeMem (InputText
, InputWidth
, L
' ');
499 if (MenuOption
->Sequence
== 2) {
500 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
502 InputText
[InputWidth
+ 1] = DATE_SEPARATOR
;
504 InputText
[InputWidth
+ 2] = L
'\0';
506 PrintAt (Column
, Row
, InputText
);
507 if (MenuOption
->Sequence
== 0) {
512 if (Question
->Operand
== EFI_IFR_TIME_OP
) {
515 if (MenuOption
->Sequence
== 0) {
516 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
517 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
519 SetUnicodeMem (InputText
, InputWidth
, L
' ');
522 if (MenuOption
->Sequence
== 2) {
523 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
525 InputText
[InputWidth
+ 1] = TIME_SEPARATOR
;
527 InputText
[InputWidth
+ 2] = L
'\0';
529 PrintAt (Column
, Row
, InputText
);
530 if (MenuOption
->Sequence
== 0) {
537 // First time we enter this handler, we need to check to see if
538 // we were passed an increment or decrement directive
541 Key
.UnicodeChar
= CHAR_NULL
;
542 if (gDirection
!= 0) {
543 Key
.ScanCode
= gDirection
;
548 Status
= WaitForKeyStroke (&Key
);
551 switch (Key
.UnicodeChar
) {
555 if (Key
.UnicodeChar
== '+') {
556 Key
.ScanCode
= SCAN_RIGHT
;
558 Key
.ScanCode
= SCAN_LEFT
;
560 Key
.UnicodeChar
= CHAR_NULL
;
564 switch (Key
.ScanCode
) {
567 if (DateOrTime
&& !ManualInput
) {
569 // By setting this value, we will return back to the caller.
570 // We need to do this since an auto-refresh will destroy the adjustment
571 // based on what the real-time-clock is showing. So we always commit
572 // upon changing the value.
574 gDirection
= SCAN_DOWN
;
577 if ((Step
!= 0) && !ManualInput
) {
578 if (Key
.ScanCode
== SCAN_LEFT
) {
579 if (EditValue
>= Minimum
+ Step
) {
580 EditValue
= EditValue
- Step
;
581 } else if (EditValue
> Minimum
){
586 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
587 if (EditValue
+ Step
<= Maximum
) {
588 EditValue
= EditValue
+ Step
;
589 } else if (EditValue
< Maximum
) {
596 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
597 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
598 if (MenuOption
->Sequence
== 2) {
602 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", (UINT16
) EditValue
);
607 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
610 if (MenuOption
->Sequence
== 0) {
611 ASSERT (EraseLen
>= 2);
612 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
613 } else if (MenuOption
->Sequence
== 1) {
614 ASSERT (EraseLen
>= 1);
615 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
617 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
618 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
620 if (MenuOption
->Sequence
== 0) {
621 ASSERT (EraseLen
>= 2);
622 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
623 } else if (MenuOption
->Sequence
== 1) {
624 ASSERT (EraseLen
>= 1);
625 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
628 QuestionValue
->Value
.u64
= EditValue
;
629 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
632 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
633 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
634 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
636 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
638 if (MenuOption
->Sequence
== 0) {
639 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
640 Column
= MenuOption
->OptCol
+ 1;
643 PrintStringAt (Column
, Row
, FormattedNumber
);
645 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
646 PrintChar (RIGHT_NUMERIC_DELIMITER
);
650 goto EnterCarriageReturn
;
655 goto EnterCarriageReturn
;
658 return EFI_DEVICE_ERROR
;
668 case CHAR_CARRIAGE_RETURN
:
670 // Store Edit value back to Question
672 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
673 switch (MenuOption
->Sequence
) {
675 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
679 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
683 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
689 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
690 switch (MenuOption
->Sequence
) {
692 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
696 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
700 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
710 QuestionValue
->Value
.u64
= EditValue
;
714 // Adjust the value to the correct one.
715 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
716 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
718 if (Question
->Operand
== EFI_IFR_DATE_OP
&&
719 (MenuOption
->Sequence
== 0 || MenuOption
->Sequence
== 2)) {
720 AdjustQuestionValue (Question
, (UINT8
)MenuOption
->Sequence
);
724 // Check to see if the Value is something reasonable against consistency limitations.
725 // If not, let's kick the error specified.
727 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
728 if (EFI_ERROR (Status
)) {
730 // Input value is not valid, restore Question Value
732 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
734 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
735 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
737 // NV flag is unnecessary for RTC type of Date/Time
739 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
752 // Remove a character
754 EditValue
= PreviousNumber
[Count
- 1];
755 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
758 PrintAt (Column
, Row
, L
" ");
765 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
766 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
767 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
768 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
769 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
770 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
772 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
776 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
777 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
783 // If Count exceed input width, there is no way more is valid
785 if (Count
>= InputWidth
) {
789 // Someone typed something valid!
793 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
795 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
801 EditValue
= Key
.UnicodeChar
- L
'0';
805 if (EditValue
> Maximum
) {
806 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
807 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
808 EditValue
= PreviousNumber
[Count
];
811 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
815 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
816 PreviousNumber
[Count
] = EditValue
;
818 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
829 Get selection for OneOf and OrderedList (Left/Right will be ignored).
831 @param Selection Pointer to current selection.
832 @param MenuOption Pointer to the current input menu.
834 @retval EFI_SUCCESS If Option input is processed successfully
835 @retval EFI_DEVICE_ERROR If operation fails
839 GetSelectionInputPopUp (
840 IN UI_MENU_SELECTION
*Selection
,
841 IN UI_MENU_OPTION
*MenuOption
848 CHAR16
*TempStringPtr
;
850 UINTN TopOptionIndex
;
851 UINTN HighlightOptionIndex
;
856 UINTN PopUpMenuLines
;
857 UINTN MenuLinesInView
;
860 INT32 SavedAttribute
;
861 BOOLEAN ShowDownArrow
;
863 UINTN DimensionsWidth
;
868 EFI_HII_VALUE HiiValue
;
869 EFI_HII_VALUE
*HiiValueArray
;
871 QUESTION_OPTION
*OneOfOption
;
872 QUESTION_OPTION
*CurrentOption
;
873 FORM_BROWSER_STATEMENT
*Question
;
875 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
879 CurrentOption
= NULL
;
880 ShowDownArrow
= FALSE
;
883 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
886 Question
= MenuOption
->ThisTag
;
887 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
888 ValueArray
= Question
->BufferValue
;
889 ValueType
= Question
->ValueType
;
896 // Calculate Option count
899 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
900 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
908 Link
= GetFirstNode (&Question
->OptionListHead
);
909 while (!IsNull (&Question
->OptionListHead
, Link
)) {
910 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
914 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
919 // Prepare HiiValue array
921 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
922 ASSERT (HiiValueArray
!= NULL
);
923 Link
= GetFirstNode (&Question
->OptionListHead
);
924 for (Index
= 0; Index
< OptionCount
; Index
++) {
926 HiiValueArray
[Index
].Type
= ValueType
;
927 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
929 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
930 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
931 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
936 // Move Suppressed Option to list tail
939 for (Index
= 0; Index
< OptionCount
; Index
++) {
940 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
941 if (OneOfOption
== NULL
) {
942 return EFI_NOT_FOUND
;
945 RemoveEntryList (&OneOfOption
->Link
);
947 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
948 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
950 // This option is suppressed, insert to tail
952 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
957 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
964 // Get the number of one of options present and its size
967 HighlightOptionIndex
= 0;
968 Link
= GetFirstNode (&Question
->OptionListHead
);
969 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
970 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
972 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
973 if (StrLen (StringPtr
) > PopUpWidth
) {
974 PopUpWidth
= StrLen (StringPtr
);
976 FreePool (StringPtr
);
978 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
980 // Find current selected Option for OneOf
982 HighlightOptionIndex
= Index
;
985 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
989 // Perform popup menu initialization.
991 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
993 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
994 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
996 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
997 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
1000 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
1001 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
1002 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
1003 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
1005 MenuLinesInView
= Bottom
- Top
- 1;
1006 if (MenuLinesInView
>= PopUpMenuLines
) {
1007 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
1008 Bottom
= Top
+ PopUpMenuLines
+ 1;
1010 ShowDownArrow
= TRUE
;
1013 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
1014 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
1021 // Clear that portion of the screen
1023 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
1026 // Draw "One of" pop-up menu
1028 Character
= BOXDRAW_DOWN_RIGHT
;
1029 PrintCharAt (Start
, Top
, Character
);
1030 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1031 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1032 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
1034 Character
= BOXDRAW_HORIZONTAL
;
1037 PrintChar (Character
);
1040 Character
= BOXDRAW_DOWN_LEFT
;
1041 PrintChar (Character
);
1042 Character
= BOXDRAW_VERTICAL
;
1043 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
1044 PrintCharAt (Start
, Index
, Character
);
1045 PrintCharAt (End
- 1, Index
, Character
);
1049 // Move to top Option
1051 Link
= GetFirstNode (&Question
->OptionListHead
);
1052 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
1053 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1057 // Display the One of options
1060 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
1061 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1062 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1064 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
1066 // If the string occupies multiple lines, truncate it to fit in one line,
1067 // and append a "..." for indication.
1069 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
1070 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
1071 ASSERT ( TempStringPtr
!= NULL
);
1072 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
1073 FreePool (StringPtr
);
1074 StringPtr
= TempStringPtr
;
1075 StrCat (StringPtr
, L
"...");
1078 if (Index
== HighlightOptionIndex
) {
1080 // Highlight the selected one
1082 CurrentOption
= OneOfOption
;
1084 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
1085 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1086 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1088 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1089 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1093 FreePool (StringPtr
);
1096 Character
= BOXDRAW_UP_RIGHT
;
1097 PrintCharAt (Start
, Bottom
, Character
);
1098 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1099 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1100 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
1102 Character
= BOXDRAW_HORIZONTAL
;
1105 PrintChar (Character
);
1108 Character
= BOXDRAW_UP_LEFT
;
1109 PrintChar (Character
);
1112 // Get User selection
1114 Key
.UnicodeChar
= CHAR_NULL
;
1115 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
1116 Key
.ScanCode
= gDirection
;
1121 Status
= WaitForKeyStroke (&Key
);
1124 switch (Key
.UnicodeChar
) {
1127 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1129 // Highlight reaches the top of the popup window, scroll one menu item.
1132 ShowDownArrow
= TRUE
;
1135 if (TopOptionIndex
== 0) {
1136 ShowUpArrow
= FALSE
;
1139 if (HighlightOptionIndex
> 0) {
1140 HighlightOptionIndex
--;
1142 ASSERT (CurrentOption
!= NULL
);
1143 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
1150 // If an ordered list op-code, we will allow for a popup of +/- keys
1151 // to create an ordered list of items
1154 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1155 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1157 // Highlight reaches the bottom of the popup window, scroll one menu item.
1163 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1164 ShowDownArrow
= FALSE
;
1167 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1168 HighlightOptionIndex
++;
1170 ASSERT (CurrentOption
!= NULL
);
1171 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1177 switch (Key
.ScanCode
) {
1180 if (Key
.ScanCode
== SCAN_UP
) {
1181 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1183 // Highlight reaches the top of the popup window, scroll one menu item.
1186 ShowDownArrow
= TRUE
;
1189 if (TopOptionIndex
== 0) {
1190 ShowUpArrow
= FALSE
;
1193 if (HighlightOptionIndex
> 0) {
1194 HighlightOptionIndex
--;
1197 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1198 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1200 // Highlight reaches the bottom of the popup window, scroll one menu item.
1206 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1207 ShowDownArrow
= FALSE
;
1210 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1211 HighlightOptionIndex
++;
1217 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1220 // Restore link list order for orderedlist
1223 HiiValue
.Type
= ValueType
;
1224 HiiValue
.Value
.u64
= 0;
1225 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1226 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1227 if (HiiValue
.Value
.u64
== 0) {
1231 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1232 if (OneOfOption
== NULL
) {
1233 return EFI_NOT_FOUND
;
1236 RemoveEntryList (&OneOfOption
->Link
);
1237 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1241 FreePool (HiiValueArray
);
1242 return EFI_DEVICE_ERROR
;
1250 case CHAR_CARRIAGE_RETURN
:
1252 // return the current selection
1256 Link
= GetFirstNode (&Question
->OptionListHead
);
1257 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1258 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1260 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1263 if (Index
> Question
->MaxContainers
) {
1267 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1270 ASSERT (CurrentOption
!= NULL
);
1271 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1274 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1275 FreePool (HiiValueArray
);
1277 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1278 if (EFI_ERROR (Status
)) {
1280 // Input value is not valid, restore Question Value
1282 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1284 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1285 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1298 Wait for a key to be pressed by user.
1300 @param Key The key which is pressed by user.
1302 @retval EFI_SUCCESS The function always completed successfully.
1307 OUT EFI_INPUT_KEY
*Key
1313 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1314 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1315 } while (EFI_ERROR(Status
));