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);
219 This routine reads a numeric value from the user input.
221 @param Selection Pointer to current selection.
222 @param MenuOption Pointer to the current input menu.
224 @retval EFI_SUCCESS If numerical input is read successfully
225 @retval EFI_DEVICE_ERROR If operation fails
230 IN UI_MENU_SELECTION
*Selection
,
231 IN UI_MENU_OPTION
*MenuOption
237 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
238 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
239 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
253 EFI_HII_VALUE
*QuestionValue
;
254 FORM_BROWSER_FORM
*Form
;
255 FORM_BROWSER_FORMSET
*FormSet
;
256 FORM_BROWSER_STATEMENT
*Question
;
258 Column
= MenuOption
->OptCol
;
259 Row
= MenuOption
->Row
;
260 PreviousNumber
[0] = 0;
265 FormSet
= Selection
->FormSet
;
266 Form
= Selection
->Form
;
267 Question
= MenuOption
->ThisTag
;
268 QuestionValue
= &Question
->HiiValue
;
269 Step
= Question
->Step
;
270 Minimum
= Question
->Minimum
;
271 Maximum
= Question
->Maximum
;
274 // Only two case, user can enter to this function: Enter and +/- case.
275 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
277 ManualInput
= (BOOLEAN
)(gDirection
== 0 ? TRUE
: FALSE
);
279 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
286 // Prepare Value to be edit
290 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
294 switch (MenuOption
->Sequence
) {
298 EditValue
= QuestionValue
->Value
.date
.Month
;
304 EditValue
= QuestionValue
->Value
.date
.Day
;
310 EditValue
= QuestionValue
->Value
.date
.Year
;
316 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
320 switch (MenuOption
->Sequence
) {
324 EditValue
= QuestionValue
->Value
.time
.Hour
;
330 EditValue
= QuestionValue
->Value
.time
.Minute
;
336 EditValue
= QuestionValue
->Value
.time
.Second
;
346 EraseLen
= gOptionBlockWidth
;
347 EditValue
= QuestionValue
->Value
.u64
;
349 Maximum
= (UINT64
) -1;
353 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
354 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
361 // Enter from "Enter" input, clear the old word showing.
364 if (Question
->Operand
== EFI_IFR_NUMERIC_OP
) {
366 InputWidth
= Question
->StorageWidth
* 2;
368 switch (Question
->StorageWidth
) {
391 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
392 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
393 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
394 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
395 InputText
[InputWidth
+ 2] = L
'\0';
397 PrintAt (Column
, Row
, InputText
);
401 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
402 if (MenuOption
->Sequence
== 2) {
408 if (MenuOption
->Sequence
== 0) {
409 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
410 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
412 SetUnicodeMem (InputText
, InputWidth
, L
' ');
415 if (MenuOption
->Sequence
== 2) {
416 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
418 InputText
[InputWidth
+ 1] = DATE_SEPARATOR
;
420 InputText
[InputWidth
+ 2] = L
'\0';
422 PrintAt (Column
, Row
, InputText
);
423 if (MenuOption
->Sequence
== 0) {
428 if (Question
->Operand
== EFI_IFR_TIME_OP
) {
431 if (MenuOption
->Sequence
== 0) {
432 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
433 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
435 SetUnicodeMem (InputText
, InputWidth
, L
' ');
438 if (MenuOption
->Sequence
== 2) {
439 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
441 InputText
[InputWidth
+ 1] = TIME_SEPARATOR
;
443 InputText
[InputWidth
+ 2] = L
'\0';
445 PrintAt (Column
, Row
, InputText
);
446 if (MenuOption
->Sequence
== 0) {
453 // First time we enter this handler, we need to check to see if
454 // we were passed an increment or decrement directive
457 Key
.UnicodeChar
= CHAR_NULL
;
458 if (gDirection
!= 0) {
459 Key
.ScanCode
= gDirection
;
464 Status
= WaitForKeyStroke (&Key
);
467 switch (Key
.UnicodeChar
) {
471 if (Key
.UnicodeChar
== '+') {
472 Key
.ScanCode
= SCAN_RIGHT
;
474 Key
.ScanCode
= SCAN_LEFT
;
476 Key
.UnicodeChar
= CHAR_NULL
;
480 switch (Key
.ScanCode
) {
483 if (DateOrTime
&& !ManualInput
) {
485 // By setting this value, we will return back to the caller.
486 // We need to do this since an auto-refresh will destroy the adjustment
487 // based on what the real-time-clock is showing. So we always commit
488 // upon changing the value.
490 gDirection
= SCAN_DOWN
;
493 if ((Step
!= 0) && !ManualInput
) {
494 if (Key
.ScanCode
== SCAN_LEFT
) {
495 if (EditValue
> Step
) {
496 EditValue
= EditValue
- Step
;
500 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
501 EditValue
= EditValue
+ Step
;
502 if (EditValue
> Maximum
) {
507 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
508 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
509 if (MenuOption
->Sequence
== 2) {
513 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", (UINT16
) EditValue
);
518 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
521 if (MenuOption
->Sequence
== 0) {
522 ASSERT (EraseLen
>= 2);
523 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
524 } else if (MenuOption
->Sequence
== 1) {
525 ASSERT (EraseLen
>= 1);
526 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
528 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
529 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
531 if (MenuOption
->Sequence
== 0) {
532 ASSERT (EraseLen
>= 2);
533 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
534 } else if (MenuOption
->Sequence
== 1) {
535 ASSERT (EraseLen
>= 1);
536 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
539 QuestionValue
->Value
.u64
= EditValue
;
540 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
543 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
544 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
545 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
547 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
549 if (MenuOption
->Sequence
== 0) {
550 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
551 Column
= MenuOption
->OptCol
+ 1;
554 PrintStringAt (Column
, Row
, FormattedNumber
);
556 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
557 PrintChar (RIGHT_NUMERIC_DELIMITER
);
561 goto EnterCarriageReturn
;
566 goto EnterCarriageReturn
;
569 return EFI_DEVICE_ERROR
;
579 case CHAR_CARRIAGE_RETURN
:
581 // Store Edit value back to Question
583 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
584 switch (MenuOption
->Sequence
) {
586 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
590 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
594 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
600 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
601 switch (MenuOption
->Sequence
) {
603 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
607 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
611 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
621 QuestionValue
->Value
.u64
= EditValue
;
625 // Check to see if the Value is something reasonable against consistency limitations.
626 // If not, let's kick the error specified.
628 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
629 if (EFI_ERROR (Status
)) {
631 // Input value is not valid, restore Question Value
633 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
635 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
636 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
638 // NV flag is unnecessary for RTC type of Date/Time
640 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
653 // Remove a character
655 EditValue
= PreviousNumber
[Count
- 1];
656 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
659 PrintAt (Column
, Row
, L
" ");
666 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
667 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
668 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
669 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
670 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
671 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
673 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
677 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
678 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
684 // If Count exceed input width, there is no way more is valid
686 if (Count
>= InputWidth
) {
690 // Someone typed something valid!
694 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
696 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
702 EditValue
= Key
.UnicodeChar
- L
'0';
706 if (EditValue
> Maximum
) {
707 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
708 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
709 EditValue
= PreviousNumber
[Count
];
712 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
716 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
717 PreviousNumber
[Count
] = EditValue
;
719 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
730 Get selection for OneOf and OrderedList (Left/Right will be ignored).
732 @param Selection Pointer to current selection.
733 @param MenuOption Pointer to the current input menu.
735 @retval EFI_SUCCESS If Option input is processed successfully
736 @retval EFI_DEVICE_ERROR If operation fails
740 GetSelectionInputPopUp (
741 IN UI_MENU_SELECTION
*Selection
,
742 IN UI_MENU_OPTION
*MenuOption
749 CHAR16
*TempStringPtr
;
751 UINTN TopOptionIndex
;
752 UINTN HighlightOptionIndex
;
757 UINTN PopUpMenuLines
;
758 UINTN MenuLinesInView
;
761 INT32 SavedAttribute
;
762 BOOLEAN ShowDownArrow
;
764 UINTN DimensionsWidth
;
769 EFI_HII_VALUE HiiValue
;
770 EFI_HII_VALUE
*HiiValueArray
;
772 QUESTION_OPTION
*OneOfOption
;
773 QUESTION_OPTION
*CurrentOption
;
774 FORM_BROWSER_STATEMENT
*Question
;
776 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
780 CurrentOption
= NULL
;
781 ShowDownArrow
= FALSE
;
784 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
787 Question
= MenuOption
->ThisTag
;
788 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
789 ValueArray
= Question
->BufferValue
;
790 ValueType
= Question
->ValueType
;
797 // Calculate Option count
800 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
801 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
809 Link
= GetFirstNode (&Question
->OptionListHead
);
810 while (!IsNull (&Question
->OptionListHead
, Link
)) {
811 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
815 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
820 // Prepare HiiValue array
822 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
823 ASSERT (HiiValueArray
!= NULL
);
824 Link
= GetFirstNode (&Question
->OptionListHead
);
825 for (Index
= 0; Index
< OptionCount
; Index
++) {
827 HiiValueArray
[Index
].Type
= ValueType
;
828 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
830 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
831 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
832 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
837 // Move Suppressed Option to list tail
840 for (Index
= 0; Index
< OptionCount
; Index
++) {
841 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
842 if (OneOfOption
== NULL
) {
843 return EFI_NOT_FOUND
;
846 RemoveEntryList (&OneOfOption
->Link
);
848 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
849 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
851 // This option is suppressed, insert to tail
853 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
858 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
865 // Get the number of one of options present and its size
868 HighlightOptionIndex
= 0;
869 Link
= GetFirstNode (&Question
->OptionListHead
);
870 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
871 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
873 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
874 if (StrLen (StringPtr
) > PopUpWidth
) {
875 PopUpWidth
= StrLen (StringPtr
);
877 FreePool (StringPtr
);
879 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
881 // Find current selected Option for OneOf
883 HighlightOptionIndex
= Index
;
886 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
890 // Perform popup menu initialization.
892 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
894 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
895 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
897 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
898 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
901 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
902 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
903 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
904 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
906 MenuLinesInView
= Bottom
- Top
- 1;
907 if (MenuLinesInView
>= PopUpMenuLines
) {
908 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
909 Bottom
= Top
+ PopUpMenuLines
+ 1;
911 ShowDownArrow
= TRUE
;
914 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
915 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
922 // Clear that portion of the screen
924 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
927 // Draw "One of" pop-up menu
929 Character
= BOXDRAW_DOWN_RIGHT
;
930 PrintCharAt (Start
, Top
, Character
);
931 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
932 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
933 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
935 Character
= BOXDRAW_HORIZONTAL
;
938 PrintChar (Character
);
941 Character
= BOXDRAW_DOWN_LEFT
;
942 PrintChar (Character
);
943 Character
= BOXDRAW_VERTICAL
;
944 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
945 PrintCharAt (Start
, Index
, Character
);
946 PrintCharAt (End
- 1, Index
, Character
);
950 // Move to top Option
952 Link
= GetFirstNode (&Question
->OptionListHead
);
953 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
954 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
958 // Display the One of options
961 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
962 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
963 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
965 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
967 // If the string occupies multiple lines, truncate it to fit in one line,
968 // and append a "..." for indication.
970 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
971 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
972 ASSERT ( TempStringPtr
!= NULL
);
973 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
974 FreePool (StringPtr
);
975 StringPtr
= TempStringPtr
;
976 StrCat (StringPtr
, L
"...");
979 if (Index
== HighlightOptionIndex
) {
981 // Highlight the selected one
983 CurrentOption
= OneOfOption
;
985 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
986 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
987 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
989 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
990 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
994 FreePool (StringPtr
);
997 Character
= BOXDRAW_UP_RIGHT
;
998 PrintCharAt (Start
, Bottom
, Character
);
999 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1000 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1001 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
1003 Character
= BOXDRAW_HORIZONTAL
;
1006 PrintChar (Character
);
1009 Character
= BOXDRAW_UP_LEFT
;
1010 PrintChar (Character
);
1013 // Get User selection
1015 Key
.UnicodeChar
= CHAR_NULL
;
1016 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
1017 Key
.ScanCode
= gDirection
;
1022 Status
= WaitForKeyStroke (&Key
);
1025 switch (Key
.UnicodeChar
) {
1028 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1030 // Highlight reaches the top of the popup window, scroll one menu item.
1033 ShowDownArrow
= TRUE
;
1036 if (TopOptionIndex
== 0) {
1037 ShowUpArrow
= FALSE
;
1040 if (HighlightOptionIndex
> 0) {
1041 HighlightOptionIndex
--;
1043 ASSERT (CurrentOption
!= NULL
);
1044 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
1051 // If an ordered list op-code, we will allow for a popup of +/- keys
1052 // to create an ordered list of items
1055 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1056 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1058 // Highlight reaches the bottom of the popup window, scroll one menu item.
1064 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1065 ShowDownArrow
= FALSE
;
1068 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1069 HighlightOptionIndex
++;
1071 ASSERT (CurrentOption
!= NULL
);
1072 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1078 switch (Key
.ScanCode
) {
1081 if (Key
.ScanCode
== SCAN_UP
) {
1082 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1084 // Highlight reaches the top of the popup window, scroll one menu item.
1087 ShowDownArrow
= TRUE
;
1090 if (TopOptionIndex
== 0) {
1091 ShowUpArrow
= FALSE
;
1094 if (HighlightOptionIndex
> 0) {
1095 HighlightOptionIndex
--;
1098 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1099 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1101 // Highlight reaches the bottom of the popup window, scroll one menu item.
1107 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1108 ShowDownArrow
= FALSE
;
1111 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1112 HighlightOptionIndex
++;
1118 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1121 // Restore link list order for orderedlist
1124 HiiValue
.Type
= ValueType
;
1125 HiiValue
.Value
.u64
= 0;
1126 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1127 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1128 if (HiiValue
.Value
.u64
== 0) {
1132 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1133 if (OneOfOption
== NULL
) {
1134 return EFI_NOT_FOUND
;
1137 RemoveEntryList (&OneOfOption
->Link
);
1138 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1142 FreePool (HiiValueArray
);
1143 return EFI_DEVICE_ERROR
;
1151 case CHAR_CARRIAGE_RETURN
:
1153 // return the current selection
1157 Link
= GetFirstNode (&Question
->OptionListHead
);
1158 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1159 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1161 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1164 if (Index
> Question
->MaxContainers
) {
1168 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1171 ASSERT (CurrentOption
!= NULL
);
1172 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1175 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1176 FreePool (HiiValueArray
);
1178 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1179 if (EFI_ERROR (Status
)) {
1181 // Input value is not valid, restore Question Value
1183 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1185 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1186 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1199 Wait for a key to be pressed by user.
1201 @param Key The key which is pressed by user.
1203 @retval EFI_SUCCESS The function always completed successfully.
1208 OUT EFI_INPUT_KEY
*Key
1214 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1215 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1216 } while (EFI_ERROR(Status
));