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
;
302 switch (QuestionValue
->Value
.date
.Month
) {
304 if ((QuestionValue
->Value
.date
.Year
% 4) == 0 &&
305 ((QuestionValue
->Value
.date
.Year
% 100) != 0 ||
306 (QuestionValue
->Value
.date
.Year
% 400) == 0)) {
324 EditValue
= QuestionValue
->Value
.date
.Day
;
330 EditValue
= QuestionValue
->Value
.date
.Year
;
336 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
340 switch (MenuOption
->Sequence
) {
344 EditValue
= QuestionValue
->Value
.time
.Hour
;
350 EditValue
= QuestionValue
->Value
.time
.Minute
;
356 EditValue
= QuestionValue
->Value
.time
.Second
;
366 EraseLen
= gOptionBlockWidth
;
367 EditValue
= QuestionValue
->Value
.u64
;
369 Maximum
= (UINT64
) -1;
373 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
374 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
381 // Enter from "Enter" input, clear the old word showing.
384 if (Question
->Operand
== EFI_IFR_NUMERIC_OP
) {
386 InputWidth
= Question
->StorageWidth
* 2;
388 switch (Question
->StorageWidth
) {
411 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
412 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
413 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
414 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
415 InputText
[InputWidth
+ 2] = L
'\0';
417 PrintAt (Column
, Row
, InputText
);
421 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
422 if (MenuOption
->Sequence
== 2) {
428 if (MenuOption
->Sequence
== 0) {
429 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
430 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
432 SetUnicodeMem (InputText
, InputWidth
, L
' ');
435 if (MenuOption
->Sequence
== 2) {
436 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
438 InputText
[InputWidth
+ 1] = DATE_SEPARATOR
;
440 InputText
[InputWidth
+ 2] = L
'\0';
442 PrintAt (Column
, Row
, InputText
);
443 if (MenuOption
->Sequence
== 0) {
448 if (Question
->Operand
== EFI_IFR_TIME_OP
) {
451 if (MenuOption
->Sequence
== 0) {
452 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
453 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
455 SetUnicodeMem (InputText
, InputWidth
, L
' ');
458 if (MenuOption
->Sequence
== 2) {
459 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
461 InputText
[InputWidth
+ 1] = TIME_SEPARATOR
;
463 InputText
[InputWidth
+ 2] = L
'\0';
465 PrintAt (Column
, Row
, InputText
);
466 if (MenuOption
->Sequence
== 0) {
473 // First time we enter this handler, we need to check to see if
474 // we were passed an increment or decrement directive
477 Key
.UnicodeChar
= CHAR_NULL
;
478 if (gDirection
!= 0) {
479 Key
.ScanCode
= gDirection
;
484 Status
= WaitForKeyStroke (&Key
);
487 switch (Key
.UnicodeChar
) {
491 if (Key
.UnicodeChar
== '+') {
492 Key
.ScanCode
= SCAN_RIGHT
;
494 Key
.ScanCode
= SCAN_LEFT
;
496 Key
.UnicodeChar
= CHAR_NULL
;
500 switch (Key
.ScanCode
) {
503 if (DateOrTime
&& !ManualInput
) {
505 // By setting this value, we will return back to the caller.
506 // We need to do this since an auto-refresh will destroy the adjustment
507 // based on what the real-time-clock is showing. So we always commit
508 // upon changing the value.
510 gDirection
= SCAN_DOWN
;
513 if ((Step
!= 0) && !ManualInput
) {
514 if (Key
.ScanCode
== SCAN_LEFT
) {
515 if (EditValue
>= Minimum
+ Step
) {
516 EditValue
= EditValue
- Step
;
517 } else if (EditValue
> Minimum
){
522 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
523 if (EditValue
+ Step
<= Maximum
) {
524 EditValue
= EditValue
+ Step
;
525 } else if (EditValue
< Maximum
) {
532 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
533 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
534 if (MenuOption
->Sequence
== 2) {
538 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", (UINT16
) EditValue
);
543 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
546 if (MenuOption
->Sequence
== 0) {
547 ASSERT (EraseLen
>= 2);
548 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
549 } else if (MenuOption
->Sequence
== 1) {
550 ASSERT (EraseLen
>= 1);
551 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
553 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
554 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
556 if (MenuOption
->Sequence
== 0) {
557 ASSERT (EraseLen
>= 2);
558 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
559 } else if (MenuOption
->Sequence
== 1) {
560 ASSERT (EraseLen
>= 1);
561 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
564 QuestionValue
->Value
.u64
= EditValue
;
565 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
568 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextColor
) | FIELD_BACKGROUND
);
569 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
570 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
572 gST
->ConOut
->SetAttribute (gST
->ConOut
, PcdGet8 (PcdBrowserFieldTextHighlightColor
) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor
));
574 if (MenuOption
->Sequence
== 0) {
575 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
576 Column
= MenuOption
->OptCol
+ 1;
579 PrintStringAt (Column
, Row
, FormattedNumber
);
581 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
582 PrintChar (RIGHT_NUMERIC_DELIMITER
);
586 goto EnterCarriageReturn
;
591 goto EnterCarriageReturn
;
594 return EFI_DEVICE_ERROR
;
604 case CHAR_CARRIAGE_RETURN
:
606 // Store Edit value back to Question
608 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
609 switch (MenuOption
->Sequence
) {
611 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
615 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
619 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
625 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
626 switch (MenuOption
->Sequence
) {
628 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
632 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
636 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
646 QuestionValue
->Value
.u64
= EditValue
;
650 // Check to see if the Value is something reasonable against consistency limitations.
651 // If not, let's kick the error specified.
653 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
654 if (EFI_ERROR (Status
)) {
656 // Input value is not valid, restore Question Value
658 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
660 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
661 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
663 // NV flag is unnecessary for RTC type of Date/Time
665 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
678 // Remove a character
680 EditValue
= PreviousNumber
[Count
- 1];
681 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
684 PrintAt (Column
, Row
, L
" ");
691 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
692 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
693 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
694 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
695 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
696 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
698 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
702 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
703 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
709 // If Count exceed input width, there is no way more is valid
711 if (Count
>= InputWidth
) {
715 // Someone typed something valid!
719 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
721 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
727 EditValue
= Key
.UnicodeChar
- L
'0';
731 if (EditValue
> Maximum
) {
732 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
733 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
734 EditValue
= PreviousNumber
[Count
];
737 UpdateStatusBar (Selection
, INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
741 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
742 PreviousNumber
[Count
] = EditValue
;
744 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
755 Get selection for OneOf and OrderedList (Left/Right will be ignored).
757 @param Selection Pointer to current selection.
758 @param MenuOption Pointer to the current input menu.
760 @retval EFI_SUCCESS If Option input is processed successfully
761 @retval EFI_DEVICE_ERROR If operation fails
765 GetSelectionInputPopUp (
766 IN UI_MENU_SELECTION
*Selection
,
767 IN UI_MENU_OPTION
*MenuOption
774 CHAR16
*TempStringPtr
;
776 UINTN TopOptionIndex
;
777 UINTN HighlightOptionIndex
;
782 UINTN PopUpMenuLines
;
783 UINTN MenuLinesInView
;
786 INT32 SavedAttribute
;
787 BOOLEAN ShowDownArrow
;
789 UINTN DimensionsWidth
;
794 EFI_HII_VALUE HiiValue
;
795 EFI_HII_VALUE
*HiiValueArray
;
797 QUESTION_OPTION
*OneOfOption
;
798 QUESTION_OPTION
*CurrentOption
;
799 FORM_BROWSER_STATEMENT
*Question
;
801 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
805 CurrentOption
= NULL
;
806 ShowDownArrow
= FALSE
;
809 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
812 Question
= MenuOption
->ThisTag
;
813 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
814 ValueArray
= Question
->BufferValue
;
815 ValueType
= Question
->ValueType
;
822 // Calculate Option count
825 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
826 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
834 Link
= GetFirstNode (&Question
->OptionListHead
);
835 while (!IsNull (&Question
->OptionListHead
, Link
)) {
836 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
840 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
845 // Prepare HiiValue array
847 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
848 ASSERT (HiiValueArray
!= NULL
);
849 Link
= GetFirstNode (&Question
->OptionListHead
);
850 for (Index
= 0; Index
< OptionCount
; Index
++) {
852 HiiValueArray
[Index
].Type
= ValueType
;
853 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
855 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
856 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
857 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
862 // Move Suppressed Option to list tail
865 for (Index
= 0; Index
< OptionCount
; Index
++) {
866 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
867 if (OneOfOption
== NULL
) {
868 return EFI_NOT_FOUND
;
871 RemoveEntryList (&OneOfOption
->Link
);
873 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
874 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
876 // This option is suppressed, insert to tail
878 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
883 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
890 // Get the number of one of options present and its size
893 HighlightOptionIndex
= 0;
894 Link
= GetFirstNode (&Question
->OptionListHead
);
895 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
896 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
898 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
899 if (StrLen (StringPtr
) > PopUpWidth
) {
900 PopUpWidth
= StrLen (StringPtr
);
902 FreePool (StringPtr
);
904 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
906 // Find current selected Option for OneOf
908 HighlightOptionIndex
= Index
;
911 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
915 // Perform popup menu initialization.
917 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
919 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
920 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
922 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
923 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
926 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
927 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
928 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
929 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
931 MenuLinesInView
= Bottom
- Top
- 1;
932 if (MenuLinesInView
>= PopUpMenuLines
) {
933 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
934 Bottom
= Top
+ PopUpMenuLines
+ 1;
936 ShowDownArrow
= TRUE
;
939 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
940 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
947 // Clear that portion of the screen
949 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
952 // Draw "One of" pop-up menu
954 Character
= BOXDRAW_DOWN_RIGHT
;
955 PrintCharAt (Start
, Top
, Character
);
956 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
957 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
958 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
960 Character
= BOXDRAW_HORIZONTAL
;
963 PrintChar (Character
);
966 Character
= BOXDRAW_DOWN_LEFT
;
967 PrintChar (Character
);
968 Character
= BOXDRAW_VERTICAL
;
969 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
970 PrintCharAt (Start
, Index
, Character
);
971 PrintCharAt (End
- 1, Index
, Character
);
975 // Move to top Option
977 Link
= GetFirstNode (&Question
->OptionListHead
);
978 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
979 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
983 // Display the One of options
986 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
987 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
988 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
990 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
992 // If the string occupies multiple lines, truncate it to fit in one line,
993 // and append a "..." for indication.
995 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
996 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
997 ASSERT ( TempStringPtr
!= NULL
);
998 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
999 FreePool (StringPtr
);
1000 StringPtr
= TempStringPtr
;
1001 StrCat (StringPtr
, L
"...");
1004 if (Index
== HighlightOptionIndex
) {
1006 // Highlight the selected one
1008 CurrentOption
= OneOfOption
;
1010 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
1011 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1012 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1014 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
1015 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
1019 FreePool (StringPtr
);
1022 Character
= BOXDRAW_UP_RIGHT
;
1023 PrintCharAt (Start
, Bottom
, Character
);
1024 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
1025 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
1026 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
1028 Character
= BOXDRAW_HORIZONTAL
;
1031 PrintChar (Character
);
1034 Character
= BOXDRAW_UP_LEFT
;
1035 PrintChar (Character
);
1038 // Get User selection
1040 Key
.UnicodeChar
= CHAR_NULL
;
1041 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
1042 Key
.ScanCode
= gDirection
;
1047 Status
= WaitForKeyStroke (&Key
);
1050 switch (Key
.UnicodeChar
) {
1053 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1055 // Highlight reaches the top of the popup window, scroll one menu item.
1058 ShowDownArrow
= TRUE
;
1061 if (TopOptionIndex
== 0) {
1062 ShowUpArrow
= FALSE
;
1065 if (HighlightOptionIndex
> 0) {
1066 HighlightOptionIndex
--;
1068 ASSERT (CurrentOption
!= NULL
);
1069 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
1076 // If an ordered list op-code, we will allow for a popup of +/- keys
1077 // to create an ordered list of items
1080 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1081 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1083 // Highlight reaches the bottom of the popup window, scroll one menu item.
1089 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1090 ShowDownArrow
= FALSE
;
1093 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1094 HighlightOptionIndex
++;
1096 ASSERT (CurrentOption
!= NULL
);
1097 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1103 switch (Key
.ScanCode
) {
1106 if (Key
.ScanCode
== SCAN_UP
) {
1107 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1109 // Highlight reaches the top of the popup window, scroll one menu item.
1112 ShowDownArrow
= TRUE
;
1115 if (TopOptionIndex
== 0) {
1116 ShowUpArrow
= FALSE
;
1119 if (HighlightOptionIndex
> 0) {
1120 HighlightOptionIndex
--;
1123 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1124 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1126 // Highlight reaches the bottom of the popup window, scroll one menu item.
1132 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1133 ShowDownArrow
= FALSE
;
1136 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1137 HighlightOptionIndex
++;
1143 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1146 // Restore link list order for orderedlist
1149 HiiValue
.Type
= ValueType
;
1150 HiiValue
.Value
.u64
= 0;
1151 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1152 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1153 if (HiiValue
.Value
.u64
== 0) {
1157 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1158 if (OneOfOption
== NULL
) {
1159 return EFI_NOT_FOUND
;
1162 RemoveEntryList (&OneOfOption
->Link
);
1163 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1167 FreePool (HiiValueArray
);
1168 return EFI_DEVICE_ERROR
;
1176 case CHAR_CARRIAGE_RETURN
:
1178 // return the current selection
1182 Link
= GetFirstNode (&Question
->OptionListHead
);
1183 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1184 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1186 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1189 if (Index
> Question
->MaxContainers
) {
1193 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1196 ASSERT (CurrentOption
!= NULL
);
1197 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1200 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1201 FreePool (HiiValueArray
);
1203 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1204 if (EFI_ERROR (Status
)) {
1206 // Input value is not valid, restore Question Value
1208 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1210 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1211 UpdateStatusBar (Selection
, NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1224 Wait for a key to be pressed by user.
1226 @param Key The key which is pressed by user.
1228 @retval EFI_SUCCESS The function always completed successfully.
1233 OUT EFI_INPUT_KEY
*Key
1239 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1240 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1241 } while (EFI_ERROR(Status
));