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
;
732 ValueType
= Question
->ValueType
;
739 // Calculate Option count
742 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
743 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
751 Link
= GetFirstNode (&Question
->OptionListHead
);
752 while (!IsNull (&Question
->OptionListHead
, Link
)) {
753 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
757 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
762 // Prepare HiiValue array
764 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
765 ASSERT (HiiValueArray
!= NULL
);
766 Link
= GetFirstNode (&Question
->OptionListHead
);
767 for (Index
= 0; Index
< OptionCount
; Index
++) {
769 HiiValueArray
[Index
].Type
= ValueType
;
770 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
772 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
773 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
774 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
779 // Move Suppressed Option to list tail
782 for (Index
= 0; Index
< OptionCount
; Index
++) {
783 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
784 if (OneOfOption
== NULL
) {
785 return EFI_NOT_FOUND
;
788 RemoveEntryList (&OneOfOption
->Link
);
790 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
791 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
793 // This option is suppressed, insert to tail
795 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
800 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
807 // Get the number of one of options present and its size
810 HighlightOptionIndex
= 0;
811 Link
= GetFirstNode (&Question
->OptionListHead
);
812 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
813 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
815 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
816 if (StrLen (StringPtr
) > PopUpWidth
) {
817 PopUpWidth
= StrLen (StringPtr
);
819 FreePool (StringPtr
);
821 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
823 // Find current selected Option for OneOf
825 HighlightOptionIndex
= Index
;
828 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
832 // Perform popup menu initialization.
834 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
836 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
837 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
839 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
840 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
843 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
844 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
845 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
846 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
848 MenuLinesInView
= Bottom
- Top
- 1;
849 if (MenuLinesInView
>= PopUpMenuLines
) {
850 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
851 Bottom
= Top
+ PopUpMenuLines
+ 1;
853 ShowDownArrow
= TRUE
;
856 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
857 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
864 // Clear that portion of the screen
866 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
869 // Draw "One of" pop-up menu
871 Character
= BOXDRAW_DOWN_RIGHT
;
872 PrintCharAt (Start
, Top
, Character
);
873 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
874 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
875 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
877 Character
= BOXDRAW_HORIZONTAL
;
880 PrintChar (Character
);
883 Character
= BOXDRAW_DOWN_LEFT
;
884 PrintChar (Character
);
885 Character
= BOXDRAW_VERTICAL
;
886 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
887 PrintCharAt (Start
, Index
, Character
);
888 PrintCharAt (End
- 1, Index
, Character
);
892 // Move to top Option
894 Link
= GetFirstNode (&Question
->OptionListHead
);
895 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
896 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
900 // Display the One of options
903 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
904 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
905 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
907 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
909 // If the string occupies multiple lines, truncate it to fit in one line,
910 // and append a "..." for indication.
912 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
913 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
914 ASSERT ( TempStringPtr
!= NULL
);
915 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
916 FreePool (StringPtr
);
917 StringPtr
= TempStringPtr
;
918 StrCat (StringPtr
, L
"...");
921 if (Index
== HighlightOptionIndex
) {
923 // Highlight the selected one
925 CurrentOption
= OneOfOption
;
927 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
928 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
929 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
931 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
932 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
936 FreePool (StringPtr
);
939 Character
= BOXDRAW_UP_RIGHT
;
940 PrintCharAt (Start
, Bottom
, Character
);
941 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
942 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
943 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
945 Character
= BOXDRAW_HORIZONTAL
;
948 PrintChar (Character
);
951 Character
= BOXDRAW_UP_LEFT
;
952 PrintChar (Character
);
955 // Get User selection
957 Key
.UnicodeChar
= CHAR_NULL
;
958 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
959 Key
.ScanCode
= gDirection
;
964 Status
= WaitForKeyStroke (&Key
);
967 switch (Key
.UnicodeChar
) {
970 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
972 // Highlight reaches the top of the popup window, scroll one menu item.
975 ShowDownArrow
= TRUE
;
978 if (TopOptionIndex
== 0) {
982 if (HighlightOptionIndex
> 0) {
983 HighlightOptionIndex
--;
985 ASSERT (CurrentOption
!= NULL
);
986 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
993 // If an ordered list op-code, we will allow for a popup of +/- keys
994 // to create an ordered list of items
997 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
998 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1000 // Highlight reaches the bottom of the popup window, scroll one menu item.
1006 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1007 ShowDownArrow
= FALSE
;
1010 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1011 HighlightOptionIndex
++;
1013 ASSERT (CurrentOption
!= NULL
);
1014 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1020 switch (Key
.ScanCode
) {
1023 if (Key
.ScanCode
== SCAN_UP
) {
1024 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1026 // Highlight reaches the top of the popup window, scroll one menu item.
1029 ShowDownArrow
= TRUE
;
1032 if (TopOptionIndex
== 0) {
1033 ShowUpArrow
= FALSE
;
1036 if (HighlightOptionIndex
> 0) {
1037 HighlightOptionIndex
--;
1040 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1041 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1043 // Highlight reaches the bottom of the popup window, scroll one menu item.
1049 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1050 ShowDownArrow
= FALSE
;
1053 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1054 HighlightOptionIndex
++;
1060 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1063 // Restore link list order for orderedlist
1066 HiiValue
.Type
= ValueType
;
1067 HiiValue
.Value
.u64
= 0;
1068 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1069 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1070 if (HiiValue
.Value
.u64
== 0) {
1074 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1075 if (OneOfOption
== NULL
) {
1076 return EFI_NOT_FOUND
;
1079 RemoveEntryList (&OneOfOption
->Link
);
1080 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1084 FreePool (HiiValueArray
);
1085 return EFI_DEVICE_ERROR
;
1093 case CHAR_CARRIAGE_RETURN
:
1095 // return the current selection
1099 Link
= GetFirstNode (&Question
->OptionListHead
);
1100 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1101 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1103 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1106 if (Index
> Question
->MaxContainers
) {
1110 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1113 ASSERT (CurrentOption
!= NULL
);
1114 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1117 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1118 FreePool (HiiValueArray
);
1120 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1121 if (EFI_ERROR (Status
)) {
1123 // Input value is not valid, restore Question Value
1125 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1127 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1128 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1141 Wait for a key to be pressed by user.
1143 @param Key The key which is pressed by user.
1145 @retval EFI_SUCCESS The function always completed successfully.
1150 OUT EFI_INPUT_KEY
*Key
1156 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1157 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1158 } while (EFI_ERROR(Status
));