2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2009, Intel Corporation
5 All rights reserved. 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.
20 Get string or password input from user.
22 @param MenuOption Pointer to the current input menu.
23 @param Prompt The prompt string shown on popup window.
24 @param StringPtr Destination for use input string.
26 @retval EFI_SUCCESS If string input is read successfully
27 @retval EFI_DEVICE_ERROR If operation fails
32 IN UI_MENU_OPTION
*MenuOption
,
44 CHAR16
*BufferedString
;
49 UINTN DimensionsWidth
;
50 UINTN DimensionsHeight
;
51 BOOLEAN CursorVisible
;
54 FORM_BROWSER_STATEMENT
*Question
;
57 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
58 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
60 NullCharacter
= CHAR_NULL
;
61 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
65 Question
= MenuOption
->ThisTag
;
66 Minimum
= (UINTN
) Question
->Minimum
;
67 Maximum
= (UINTN
) Question
->Maximum
;
69 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
75 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
78 if (ScreenSize
< (Maximum
+ 1)) {
79 ScreenSize
= Maximum
+ 1;
82 if ((ScreenSize
+ 2) > DimensionsWidth
) {
83 ScreenSize
= DimensionsWidth
- 2;
86 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
87 ASSERT (BufferedString
);
89 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
90 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
93 // Display prompt for string
95 CreateMultiStringPopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
97 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
99 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
100 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
103 Status
= WaitForKeyStroke (&Key
);
104 ASSERT_EFI_ERROR (Status
);
106 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
107 switch (Key
.UnicodeChar
) {
109 switch (Key
.ScanCode
) {
117 FreePool (TempString
);
118 FreePool (BufferedString
);
119 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
120 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
121 return EFI_DEVICE_ERROR
;
129 case CHAR_CARRIAGE_RETURN
:
130 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
132 FreePool (TempString
);
133 FreePool (BufferedString
);
134 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
135 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
139 // Simply create a popup to tell the user that they had typed in too few characters.
140 // To save code space, we can then treat this as an error and return back to the menu.
143 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
144 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
146 FreePool (TempString
);
147 FreePool (BufferedString
);
148 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
149 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
150 return EFI_DEVICE_ERROR
;
156 if (StringPtr
[0] != CHAR_NULL
) {
157 for (Index
= 0; StringPtr
[Index
] != CHAR_NULL
; Index
++) {
158 TempString
[Index
] = StringPtr
[Index
];
161 // Effectively truncate string by 1 character
163 TempString
[Index
- 1] = CHAR_NULL
;
164 StrCpy (StringPtr
, TempString
);
169 // If it is the beginning of the string, don't worry about checking maximum limits
171 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
172 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
173 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
174 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
175 KeyPad
[0] = Key
.UnicodeChar
;
176 KeyPad
[1] = CHAR_NULL
;
177 StrCat (StringPtr
, KeyPad
);
178 StrCat (TempString
, KeyPad
);
182 // If the width of the input string is now larger than the screen, we nee to
183 // adjust the index to start printing portions of the string
185 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
186 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
188 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
189 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
195 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
198 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
199 BufferedString
[Count
] = StringPtr
[Index
];
207 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
212 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
213 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
220 This routine reads a numeric value from the user input.
222 @param Selection Pointer to current selection.
223 @param MenuOption Pointer to the current input menu.
225 @retval EFI_SUCCESS If numerical input is read successfully
226 @retval EFI_DEVICE_ERROR If operation fails
231 IN UI_MENU_SELECTION
*Selection
,
232 IN UI_MENU_OPTION
*MenuOption
238 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
239 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
240 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
254 EFI_HII_VALUE
*QuestionValue
;
255 FORM_BROWSER_FORM
*Form
;
256 FORM_BROWSER_FORMSET
*FormSet
;
257 FORM_BROWSER_STATEMENT
*Question
;
259 Column
= MenuOption
->OptCol
;
260 Row
= MenuOption
->Row
;
261 PreviousNumber
[0] = 0;
266 FormSet
= Selection
->FormSet
;
267 Form
= Selection
->Form
;
268 Question
= MenuOption
->ThisTag
;
269 QuestionValue
= &Question
->HiiValue
;
270 Step
= Question
->Step
;
271 Minimum
= Question
->Minimum
;
272 Maximum
= Question
->Maximum
;
274 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
281 // Prepare Value to be edit
285 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
289 switch (MenuOption
->Sequence
) {
293 EditValue
= QuestionValue
->Value
.date
.Month
;
299 EditValue
= QuestionValue
->Value
.date
.Day
;
305 EditValue
= QuestionValue
->Value
.date
.Year
;
311 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
315 switch (MenuOption
->Sequence
) {
319 EditValue
= QuestionValue
->Value
.time
.Hour
;
325 EditValue
= QuestionValue
->Value
.time
.Minute
;
331 EditValue
= QuestionValue
->Value
.time
.Second
;
341 EraseLen
= gOptionBlockWidth
;
342 EditValue
= QuestionValue
->Value
.u64
;
344 Maximum
= (UINT64
) -1;
354 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
355 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
363 InputWidth
= Question
->StorageWidth
* 2;
365 switch (Question
->StorageWidth
) {
388 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
389 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
390 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
391 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
392 InputText
[InputWidth
+ 2] = L
'\0';
394 PrintAt (Column
, Row
, InputText
);
399 // First time we enter this handler, we need to check to see if
400 // we were passed an increment or decrement directive
403 Key
.UnicodeChar
= CHAR_NULL
;
404 if (gDirection
!= 0) {
405 Key
.ScanCode
= gDirection
;
410 Status
= WaitForKeyStroke (&Key
);
413 switch (Key
.UnicodeChar
) {
417 if (Key
.UnicodeChar
== '+') {
418 Key
.ScanCode
= SCAN_RIGHT
;
420 Key
.ScanCode
= SCAN_LEFT
;
422 Key
.UnicodeChar
= CHAR_NULL
;
426 switch (Key
.ScanCode
) {
431 // By setting this value, we will return back to the caller.
432 // We need to do this since an auto-refresh will destroy the adjustment
433 // based on what the real-time-clock is showing. So we always commit
434 // upon changing the value.
436 gDirection
= SCAN_DOWN
;
440 if (Key
.ScanCode
== SCAN_LEFT
) {
441 if (EditValue
> Step
) {
442 EditValue
= EditValue
- Step
;
446 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
447 EditValue
= EditValue
+ Step
;
448 if (EditValue
> Maximum
) {
453 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
454 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
455 if (MenuOption
->Sequence
== 2) {
459 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", EditValue
);
464 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
467 if (MenuOption
->Sequence
== 0) {
468 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
469 } else if (MenuOption
->Sequence
== 1) {
470 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
472 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
473 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
475 if (MenuOption
->Sequence
== 0) {
476 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
477 } else if (MenuOption
->Sequence
== 1) {
478 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
481 QuestionValue
->Value
.u64
= EditValue
;
482 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
485 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
486 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
487 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
489 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
491 if (MenuOption
->Sequence
== 0) {
492 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
493 Column
= MenuOption
->OptCol
+ 1;
496 PrintStringAt (Column
, Row
, FormattedNumber
);
498 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
499 PrintChar (RIGHT_NUMERIC_DELIMITER
);
503 goto EnterCarriageReturn
;
508 goto EnterCarriageReturn
;
511 return EFI_DEVICE_ERROR
;
521 case CHAR_CARRIAGE_RETURN
:
523 // Store Edit value back to Question
525 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
526 switch (MenuOption
->Sequence
) {
528 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
532 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
536 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
542 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
543 switch (MenuOption
->Sequence
) {
545 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
549 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
553 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
563 QuestionValue
->Value
.u64
= EditValue
;
567 // Check to see if the Value is something reasonable against consistency limitations.
568 // If not, let's kick the error specified.
570 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
571 if (EFI_ERROR (Status
)) {
573 // Input value is not valid, restore Question Value
575 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
577 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
578 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
580 // NV flag is unnecessary for RTC type of Date/Time
582 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
595 // Remove a character
597 EditValue
= PreviousNumber
[Count
- 1];
598 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
601 PrintAt (Column
, Row
, L
" ");
608 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
609 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
610 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
611 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
612 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
613 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
615 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
619 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
620 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
626 // If Count exceed input width, there is no way more is valid
628 if (Count
>= InputWidth
) {
632 // Someone typed something valid!
636 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
638 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
644 EditValue
= Key
.UnicodeChar
- L
'0';
648 if (EditValue
> Maximum
) {
649 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
650 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
651 EditValue
= PreviousNumber
[Count
];
654 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
658 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
659 PreviousNumber
[Count
] = EditValue
;
661 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
672 Get selection for OneOf and OrderedList (Left/Right will be ignored).
674 @param Selection Pointer to current selection.
675 @param MenuOption Pointer to the current input menu.
677 @retval EFI_SUCCESS If Option input is processed successfully
678 @retval EFI_DEVICE_ERROR If operation fails
682 GetSelectionInputPopUp (
683 IN UI_MENU_SELECTION
*Selection
,
684 IN UI_MENU_OPTION
*MenuOption
691 CHAR16
*TempStringPtr
;
693 UINTN TopOptionIndex
;
694 UINTN HighlightOptionIndex
;
699 UINTN PopUpMenuLines
;
700 UINTN MenuLinesInView
;
703 INT32 SavedAttribute
;
704 BOOLEAN ShowDownArrow
;
706 UINTN DimensionsWidth
;
711 EFI_HII_VALUE HiiValue
;
712 EFI_HII_VALUE
*HiiValueArray
;
714 QUESTION_OPTION
*OneOfOption
;
715 QUESTION_OPTION
*CurrentOption
;
716 FORM_BROWSER_STATEMENT
*Question
;
718 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
722 CurrentOption
= NULL
;
723 ShowDownArrow
= FALSE
;
726 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
729 Question
= MenuOption
->ThisTag
;
730 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
731 ValueArray
= Question
->BufferValue
;
738 // Calculate Option count
741 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
742 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
750 Link
= GetFirstNode (&Question
->OptionListHead
);
751 while (!IsNull (&Question
->OptionListHead
, Link
)) {
752 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
756 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
761 // Prepare HiiValue array
763 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
764 ASSERT (HiiValueArray
!= NULL
);
765 Link
= GetFirstNode (&Question
->OptionListHead
);
766 for (Index
= 0; Index
< OptionCount
; Index
++) {
768 HiiValueArray
[Index
].Type
= ValueType
;
769 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
771 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
772 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
773 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
778 // Move Suppressed Option to list tail
781 for (Index
= 0; Index
< OptionCount
; Index
++) {
782 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
783 if (OneOfOption
== NULL
) {
784 return EFI_NOT_FOUND
;
787 RemoveEntryList (&OneOfOption
->Link
);
789 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
790 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
792 // This option is suppressed, insert to tail
794 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
799 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
806 // Get the number of one of options present and its size
809 HighlightOptionIndex
= 0;
810 Link
= GetFirstNode (&Question
->OptionListHead
);
811 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
812 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
814 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
815 if (StrLen (StringPtr
) > PopUpWidth
) {
816 PopUpWidth
= StrLen (StringPtr
);
818 FreePool (StringPtr
);
820 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
822 // Find current selected Option for OneOf
824 HighlightOptionIndex
= Index
;
827 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
831 // Perform popup menu initialization.
833 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
835 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
836 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
838 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
839 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
842 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
843 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
844 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
845 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
847 MenuLinesInView
= Bottom
- Top
- 1;
848 if (MenuLinesInView
>= PopUpMenuLines
) {
849 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
850 Bottom
= Top
+ PopUpMenuLines
+ 1;
852 ShowDownArrow
= TRUE
;
855 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
856 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
863 // Clear that portion of the screen
865 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
868 // Draw "One of" pop-up menu
870 Character
= BOXDRAW_DOWN_RIGHT
;
871 PrintCharAt (Start
, Top
, Character
);
872 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
873 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
874 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
876 Character
= BOXDRAW_HORIZONTAL
;
879 PrintChar (Character
);
882 Character
= BOXDRAW_DOWN_LEFT
;
883 PrintChar (Character
);
884 Character
= BOXDRAW_VERTICAL
;
885 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
886 PrintCharAt (Start
, Index
, Character
);
887 PrintCharAt (End
- 1, Index
, Character
);
891 // Move to top Option
893 Link
= GetFirstNode (&Question
->OptionListHead
);
894 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
895 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
899 // Display the One of options
902 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
903 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
904 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
906 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
908 // If the string occupies multiple lines, truncate it to fit in one line,
909 // and append a "..." for indication.
911 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
912 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
913 ASSERT ( TempStringPtr
!= NULL
);
914 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
915 FreePool (StringPtr
);
916 StringPtr
= TempStringPtr
;
917 StrCat (StringPtr
, L
"...");
920 if (Index
== HighlightOptionIndex
) {
922 // Highlight the selected one
924 CurrentOption
= OneOfOption
;
926 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
927 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
930 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
931 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
935 FreePool (StringPtr
);
938 Character
= BOXDRAW_UP_RIGHT
;
939 PrintCharAt (Start
, Bottom
, Character
);
940 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
941 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
942 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
944 Character
= BOXDRAW_HORIZONTAL
;
947 PrintChar (Character
);
950 Character
= BOXDRAW_UP_LEFT
;
951 PrintChar (Character
);
954 // Get User selection
956 Key
.UnicodeChar
= CHAR_NULL
;
957 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
958 Key
.ScanCode
= gDirection
;
963 Status
= WaitForKeyStroke (&Key
);
966 switch (Key
.UnicodeChar
) {
969 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
971 // Highlight reaches the top of the popup window, scroll one menu item.
974 ShowDownArrow
= TRUE
;
977 if (TopOptionIndex
== 0) {
981 if (HighlightOptionIndex
> 0) {
982 HighlightOptionIndex
--;
984 ASSERT (CurrentOption
!= NULL
);
985 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
992 // If an ordered list op-code, we will allow for a popup of +/- keys
993 // to create an ordered list of items
996 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
997 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
999 // Highlight reaches the bottom of the popup window, scroll one menu item.
1005 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1006 ShowDownArrow
= FALSE
;
1009 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1010 HighlightOptionIndex
++;
1012 ASSERT (CurrentOption
!= NULL
);
1013 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1019 switch (Key
.ScanCode
) {
1022 if (Key
.ScanCode
== SCAN_UP
) {
1023 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1025 // Highlight reaches the top of the popup window, scroll one menu item.
1028 ShowDownArrow
= TRUE
;
1031 if (TopOptionIndex
== 0) {
1032 ShowUpArrow
= FALSE
;
1035 if (HighlightOptionIndex
> 0) {
1036 HighlightOptionIndex
--;
1039 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1040 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1042 // Highlight reaches the bottom of the popup window, scroll one menu item.
1048 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1049 ShowDownArrow
= FALSE
;
1052 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1053 HighlightOptionIndex
++;
1059 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1062 // Restore link list order for orderedlist
1065 HiiValue
.Type
= ValueType
;
1066 HiiValue
.Value
.u64
= 0;
1067 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1068 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1069 if (HiiValue
.Value
.u64
== 0) {
1073 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1074 if (OneOfOption
== NULL
) {
1075 return EFI_NOT_FOUND
;
1078 RemoveEntryList (&OneOfOption
->Link
);
1079 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1083 FreePool (HiiValueArray
);
1084 return EFI_DEVICE_ERROR
;
1092 case CHAR_CARRIAGE_RETURN
:
1094 // return the current selection
1098 Link
= GetFirstNode (&Question
->OptionListHead
);
1099 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1100 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1102 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1105 if (Index
> Question
->MaxContainers
) {
1109 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1112 ASSERT (CurrentOption
!= NULL
);
1113 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1116 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1117 FreePool (HiiValueArray
);
1119 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1120 if (EFI_ERROR (Status
)) {
1122 // Input value is not valid, restore Question Value
1124 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1126 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1127 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1140 Wait for a key to be pressed by user.
1142 @param Key The key which is pressed by user.
1144 @retval EFI_SUCCESS The function always completed successfully.
1149 OUT EFI_INPUT_KEY
*Key
1155 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1156 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1157 } while (EFI_ERROR(Status
));