2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2007, 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 CreatePopUp (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
[23];
239 CHAR16 FormattedNumber
[22];
240 UINT64 PreviousNumber
[20];
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 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
391 InputText
[InputWidth
+ 2] = L
'\0';
393 PrintAt (Column
, Row
, InputText
);
398 // First time we enter this handler, we need to check to see if
399 // we were passed an increment or decrement directive
402 Key
.UnicodeChar
= CHAR_NULL
;
403 if (gDirection
!= 0) {
404 Key
.ScanCode
= gDirection
;
409 Status
= WaitForKeyStroke (&Key
);
412 switch (Key
.UnicodeChar
) {
416 if (Key
.UnicodeChar
== '+') {
417 Key
.ScanCode
= SCAN_RIGHT
;
419 Key
.ScanCode
= SCAN_LEFT
;
421 Key
.UnicodeChar
= CHAR_NULL
;
425 switch (Key
.ScanCode
) {
430 // By setting this value, we will return back to the caller.
431 // We need to do this since an auto-refresh will destroy the adjustment
432 // based on what the real-time-clock is showing. So we always commit
433 // upon changing the value.
435 gDirection
= SCAN_DOWN
;
439 if (Key
.ScanCode
== SCAN_LEFT
) {
440 if (EditValue
> Step
) {
441 EditValue
= EditValue
- Step
;
445 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
446 EditValue
= EditValue
+ Step
;
447 if (EditValue
> Maximum
) {
452 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
453 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
454 if (MenuOption
->Sequence
== 2) {
458 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", EditValue
);
463 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
466 if (MenuOption
->Sequence
== 0) {
467 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
468 } else if (MenuOption
->Sequence
== 1) {
469 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
471 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
472 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
474 if (MenuOption
->Sequence
== 0) {
475 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
476 } else if (MenuOption
->Sequence
== 1) {
477 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
480 QuestionValue
->Value
.u64
= EditValue
;
481 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
484 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
485 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
486 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
488 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
490 if (MenuOption
->Sequence
== 0) {
491 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
492 Column
= MenuOption
->OptCol
+ 1;
495 PrintStringAt (Column
, Row
, FormattedNumber
);
497 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
498 PrintChar (RIGHT_NUMERIC_DELIMITER
);
502 goto EnterCarriageReturn
;
507 goto EnterCarriageReturn
;
510 return EFI_DEVICE_ERROR
;
520 case CHAR_CARRIAGE_RETURN
:
522 // Store Edit value back to Question
524 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
525 switch (MenuOption
->Sequence
) {
527 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
531 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
535 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
541 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
542 switch (MenuOption
->Sequence
) {
544 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
548 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
552 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
562 QuestionValue
->Value
.u64
= EditValue
;
566 // Check to see if the Value is something reasonable against consistency limitations.
567 // If not, let's kick the error specified.
569 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
570 if (EFI_ERROR (Status
)) {
572 // Input value is not valid, restore Question Value
574 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
576 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
577 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
579 // NV flag is unnecessary for RTC type of Date/Time
581 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
594 // Remove a character
596 EditValue
= PreviousNumber
[Count
- 1];
597 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
600 PrintAt (Column
, Row
, L
" ");
607 if (!IsHexDigit (&Digital
, Key
.UnicodeChar
)) {
608 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
612 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
613 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
619 // If Count exceed input width, there is no way more is valid
621 if (Count
>= InputWidth
) {
625 // Someone typed something valid!
629 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
631 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
637 EditValue
= Key
.UnicodeChar
- L
'0';
641 if (EditValue
> Maximum
) {
642 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
643 EditValue
= PreviousNumber
[Count
];
646 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
650 PreviousNumber
[Count
] = EditValue
;
652 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
663 Get selection for OneOf and OrderedList (Left/Right will be ignored).
665 @param Selection Pointer to current selection.
666 @param MenuOption Pointer to the current input menu.
668 @retval EFI_SUCCESS If Option input is processed successfully
669 @retval EFI_DEVICE_ERROR If operation fails
673 GetSelectionInputPopUp (
674 IN UI_MENU_SELECTION
*Selection
,
675 IN UI_MENU_OPTION
*MenuOption
682 CHAR16
*TempStringPtr
;
684 UINTN TopOptionIndex
;
685 UINTN HighlightOptionIndex
;
690 UINTN PopUpMenuLines
;
691 UINTN MenuLinesInView
;
694 INT32 SavedAttribute
;
695 BOOLEAN ShowDownArrow
;
697 UINTN DimensionsWidth
;
701 EFI_HII_VALUE HiiValue
;
702 EFI_HII_VALUE
*HiiValueArray
;
704 QUESTION_OPTION
*OneOfOption
;
705 QUESTION_OPTION
*CurrentOption
;
706 FORM_BROWSER_STATEMENT
*Question
;
708 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
711 CurrentOption
= NULL
;
712 ShowDownArrow
= FALSE
;
715 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
718 Question
= MenuOption
->ThisTag
;
719 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
720 ValueArray
= Question
->BufferValue
;
727 // Calculate Option count
730 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
731 if (ValueArray
[Index
] == 0) {
739 Link
= GetFirstNode (&Question
->OptionListHead
);
740 while (!IsNull (&Question
->OptionListHead
, Link
)) {
741 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
745 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
750 // Prepare HiiValue array
752 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
753 ASSERT (HiiValueArray
!= NULL
);
754 Link
= GetFirstNode (&Question
->OptionListHead
);
755 for (Index
= 0; Index
< OptionCount
; Index
++) {
757 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
758 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
760 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
761 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
762 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
767 // Move Suppressed Option to list tail
770 for (Index
= 0; Index
< OptionCount
; Index
++) {
771 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
772 if (OneOfOption
== NULL
) {
773 return EFI_NOT_FOUND
;
776 RemoveEntryList (&OneOfOption
->Link
);
778 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
779 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
781 // This option is suppressed, insert to tail
783 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
788 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
795 // Get the number of one of options present and its size
798 HighlightOptionIndex
= 0;
799 Link
= GetFirstNode (&Question
->OptionListHead
);
800 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
801 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
803 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
804 if (StrLen (StringPtr
) > PopUpWidth
) {
805 PopUpWidth
= StrLen (StringPtr
);
807 FreePool (StringPtr
);
809 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
811 // Find current selected Option for OneOf
813 HighlightOptionIndex
= Index
;
816 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
820 // Perform popup menu initialization.
822 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
824 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
825 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
827 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
828 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
831 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
832 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
833 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
834 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
836 MenuLinesInView
= Bottom
- Top
- 1;
837 if (MenuLinesInView
>= PopUpMenuLines
) {
838 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
839 Bottom
= Top
+ PopUpMenuLines
+ 1;
841 ShowDownArrow
= TRUE
;
844 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
845 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
852 // Clear that portion of the screen
854 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
857 // Draw "One of" pop-up menu
859 Character
= BOXDRAW_DOWN_RIGHT
;
860 PrintCharAt (Start
, Top
, Character
);
861 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
862 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
863 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
865 Character
= BOXDRAW_HORIZONTAL
;
868 PrintChar (Character
);
871 Character
= BOXDRAW_DOWN_LEFT
;
872 PrintChar (Character
);
873 Character
= BOXDRAW_VERTICAL
;
874 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
875 PrintCharAt (Start
, Index
, Character
);
876 PrintCharAt (End
- 1, Index
, Character
);
880 // Move to top Option
882 Link
= GetFirstNode (&Question
->OptionListHead
);
883 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
884 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
888 // Display the One of options
891 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
892 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
893 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
895 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
897 // If the string occupies multiple lines, truncate it to fit in one line,
898 // and append a "..." for indication.
900 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
901 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
902 ASSERT ( TempStringPtr
!= NULL
);
903 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
904 FreePool (StringPtr
);
905 StringPtr
= TempStringPtr
;
906 StrCat (StringPtr
, L
"...");
909 if (Index
== HighlightOptionIndex
) {
911 // Highlight the selected one
913 CurrentOption
= OneOfOption
;
915 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
916 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
917 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
919 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
920 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
924 FreePool (StringPtr
);
927 Character
= BOXDRAW_UP_RIGHT
;
928 PrintCharAt (Start
, Bottom
, Character
);
929 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
930 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
931 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
933 Character
= BOXDRAW_HORIZONTAL
;
936 PrintChar (Character
);
939 Character
= BOXDRAW_UP_LEFT
;
940 PrintChar (Character
);
943 // Get User selection
945 Key
.UnicodeChar
= CHAR_NULL
;
946 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
947 Key
.ScanCode
= gDirection
;
952 Status
= WaitForKeyStroke (&Key
);
955 switch (Key
.UnicodeChar
) {
958 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
960 // Highlight reaches the top of the popup window, scroll one menu item.
963 ShowDownArrow
= TRUE
;
966 if (TopOptionIndex
== 0) {
970 if (HighlightOptionIndex
> 0) {
971 HighlightOptionIndex
--;
973 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
980 // If an ordered list op-code, we will allow for a popup of +/- keys
981 // to create an ordered list of items
984 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
985 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
987 // Highlight reaches the bottom of the popup window, scroll one menu item.
993 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
994 ShowDownArrow
= FALSE
;
997 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
998 HighlightOptionIndex
++;
1000 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1006 switch (Key
.ScanCode
) {
1009 if (Key
.ScanCode
== SCAN_UP
) {
1010 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1012 // Highlight reaches the top of the popup window, scroll one menu item.
1015 ShowDownArrow
= TRUE
;
1018 if (TopOptionIndex
== 0) {
1019 ShowUpArrow
= FALSE
;
1022 if (HighlightOptionIndex
> 0) {
1023 HighlightOptionIndex
--;
1026 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1027 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1029 // Highlight reaches the bottom of the popup window, scroll one menu item.
1035 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1036 ShowDownArrow
= FALSE
;
1039 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1040 HighlightOptionIndex
++;
1046 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1049 // Restore link list order for orderedlist
1052 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1053 HiiValue
.Value
.u64
= 0;
1054 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1055 HiiValue
.Value
.u8
= ValueArray
[Index
];
1056 if (HiiValue
.Value
.u8
) {
1060 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1061 if (OneOfOption
== NULL
) {
1062 return EFI_NOT_FOUND
;
1065 RemoveEntryList (&OneOfOption
->Link
);
1066 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1070 FreePool (HiiValueArray
);
1071 return EFI_DEVICE_ERROR
;
1079 case CHAR_CARRIAGE_RETURN
:
1081 // return the current selection
1085 Link
= GetFirstNode (&Question
->OptionListHead
);
1086 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1087 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1089 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1092 if (Index
> Question
->MaxContainers
) {
1096 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1099 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1102 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1103 FreePool (HiiValueArray
);
1105 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1106 if (EFI_ERROR (Status
)) {
1108 // Input value is not valid, restore Question Value
1110 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1112 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1113 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1126 Wait for a key to be pressed by user.
1128 @param Key The key which is pressed by user.
1130 @retval EFI_SUCCESS The function always completed successfully.
1135 OUT EFI_INPUT_KEY
*Key
1141 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1142 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1143 } while (EFI_ERROR(Status
));