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 gBS
->FreePool (TempString
);
118 gBS
->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 gBS
->FreePool (TempString
);
133 gBS
->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 gBS
->FreePool (TempString
);
147 gBS
->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
);
505 goto EnterCarriageReturn
;
508 return EFI_DEVICE_ERROR
;
518 case CHAR_CARRIAGE_RETURN
:
520 // Store Edit value back to Question
522 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
523 switch (MenuOption
->Sequence
) {
525 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
529 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
533 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
539 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
540 switch (MenuOption
->Sequence
) {
542 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
546 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
550 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
560 QuestionValue
->Value
.u64
= EditValue
;
564 // Check to see if the Value is something reasonable against consistency limitations.
565 // If not, let's kick the error specified.
567 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
568 if (EFI_ERROR (Status
)) {
570 // Input value is not valid, restore Question Value
572 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
574 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
575 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
577 // NV flag is unnecessary for RTC type of Date/Time
579 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
592 // Remove a character
594 EditValue
= PreviousNumber
[Count
- 1];
595 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
598 PrintAt (Column
, Row
, L
" ");
605 if (!IsHexDigit (&Digital
, Key
.UnicodeChar
)) {
606 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
610 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
611 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
617 // If Count exceed input width, there is no way more is valid
619 if (Count
>= InputWidth
) {
623 // Someone typed something valid!
627 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
629 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
635 EditValue
= Key
.UnicodeChar
- L
'0';
639 if (EditValue
> Maximum
) {
640 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
641 EditValue
= PreviousNumber
[Count
];
644 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
648 PreviousNumber
[Count
] = EditValue
;
650 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
661 Get selection for OneOf and OrderedList (Left/Right will be ignored).
663 @param Selection Pointer to current selection.
664 @param MenuOption Pointer to the current input menu.
666 @retval EFI_SUCCESS If Option input is processed successfully
667 @retval EFI_DEVICE_ERROR If operation fails
671 GetSelectionInputPopUp (
672 IN UI_MENU_SELECTION
*Selection
,
673 IN UI_MENU_OPTION
*MenuOption
680 CHAR16
*TempStringPtr
;
682 UINTN TopOptionIndex
;
683 UINTN HighlightOptionIndex
;
688 UINTN PopUpMenuLines
;
689 UINTN MenuLinesInView
;
692 INT32 SavedAttribute
;
693 BOOLEAN ShowDownArrow
;
695 UINTN DimensionsWidth
;
699 EFI_HII_VALUE HiiValue
;
700 EFI_HII_VALUE
*HiiValueArray
;
702 QUESTION_OPTION
*OneOfOption
;
703 QUESTION_OPTION
*CurrentOption
;
704 FORM_BROWSER_STATEMENT
*Question
;
706 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
709 CurrentOption
= NULL
;
710 ShowDownArrow
= FALSE
;
713 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
716 Question
= MenuOption
->ThisTag
;
717 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
718 ValueArray
= Question
->BufferValue
;
725 // Calculate Option count
728 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
729 if (ValueArray
[Index
] == 0) {
737 Link
= GetFirstNode (&Question
->OptionListHead
);
738 while (!IsNull (&Question
->OptionListHead
, Link
)) {
739 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
743 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
748 // Prepare HiiValue array
750 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
751 ASSERT (HiiValueArray
!= NULL
);
752 Link
= GetFirstNode (&Question
->OptionListHead
);
753 for (Index
= 0; Index
< OptionCount
; Index
++) {
755 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
756 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
758 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
759 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
760 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
765 // Move Suppressed Option to list tail
768 for (Index
= 0; Index
< OptionCount
; Index
++) {
769 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
770 if (OneOfOption
== NULL
) {
771 return EFI_NOT_FOUND
;
774 RemoveEntryList (&OneOfOption
->Link
);
776 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
777 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
779 // This option is suppressed, insert to tail
781 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
786 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
793 // Get the number of one of options present and its size
796 HighlightOptionIndex
= 0;
797 Link
= GetFirstNode (&Question
->OptionListHead
);
798 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
799 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
801 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
802 if (StrLen (StringPtr
) > PopUpWidth
) {
803 PopUpWidth
= StrLen (StringPtr
);
805 gBS
->FreePool (StringPtr
);
807 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
809 // Find current selected Option for OneOf
811 HighlightOptionIndex
= Index
;
814 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
818 // Perform popup menu initialization.
820 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
822 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
823 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
825 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
826 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
829 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
830 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
831 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
832 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
834 MenuLinesInView
= Bottom
- Top
- 1;
835 if (MenuLinesInView
>= PopUpMenuLines
) {
836 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
837 Bottom
= Top
+ PopUpMenuLines
+ 1;
839 ShowDownArrow
= TRUE
;
842 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
843 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
850 // Clear that portion of the screen
852 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
855 // Draw "One of" pop-up menu
857 Character
= BOXDRAW_DOWN_RIGHT
;
858 PrintCharAt (Start
, Top
, Character
);
859 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
860 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
861 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
863 Character
= BOXDRAW_HORIZONTAL
;
866 PrintChar (Character
);
869 Character
= BOXDRAW_DOWN_LEFT
;
870 PrintChar (Character
);
871 Character
= BOXDRAW_VERTICAL
;
872 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
873 PrintCharAt (Start
, Index
, Character
);
874 PrintCharAt (End
- 1, Index
, Character
);
878 // Move to top Option
880 Link
= GetFirstNode (&Question
->OptionListHead
);
881 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
882 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
886 // Display the One of options
889 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
890 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
891 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
893 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
895 // If the string occupies multiple lines, truncate it to fit in one line,
896 // and append a "..." for indication.
898 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
899 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
900 ASSERT ( TempStringPtr
!= NULL
);
901 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
902 gBS
->FreePool (StringPtr
);
903 StringPtr
= TempStringPtr
;
904 StrCat (StringPtr
, L
"...");
907 if (Index
== HighlightOptionIndex
) {
909 // Highlight the selected one
911 CurrentOption
= OneOfOption
;
913 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
914 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
915 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
917 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
918 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
922 gBS
->FreePool (StringPtr
);
925 Character
= BOXDRAW_UP_RIGHT
;
926 PrintCharAt (Start
, Bottom
, Character
);
927 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
928 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
929 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
931 Character
= BOXDRAW_HORIZONTAL
;
934 PrintChar (Character
);
937 Character
= BOXDRAW_UP_LEFT
;
938 PrintChar (Character
);
941 // Get User selection
943 Key
.UnicodeChar
= CHAR_NULL
;
944 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
945 Key
.ScanCode
= gDirection
;
950 Status
= WaitForKeyStroke (&Key
);
953 switch (Key
.UnicodeChar
) {
956 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
958 // Highlight reaches the top of the popup window, scroll one menu item.
961 ShowDownArrow
= TRUE
;
964 if (TopOptionIndex
== 0) {
968 if (HighlightOptionIndex
> 0) {
969 HighlightOptionIndex
--;
971 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
978 // If an ordered list op-code, we will allow for a popup of +/- keys
979 // to create an ordered list of items
982 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
983 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
985 // Highlight reaches the bottom of the popup window, scroll one menu item.
991 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
992 ShowDownArrow
= FALSE
;
995 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
996 HighlightOptionIndex
++;
998 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1004 switch (Key
.ScanCode
) {
1007 if (Key
.ScanCode
== SCAN_UP
) {
1008 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1010 // Highlight reaches the top of the popup window, scroll one menu item.
1013 ShowDownArrow
= TRUE
;
1016 if (TopOptionIndex
== 0) {
1017 ShowUpArrow
= FALSE
;
1020 if (HighlightOptionIndex
> 0) {
1021 HighlightOptionIndex
--;
1024 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1025 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1027 // Highlight reaches the bottom of the popup window, scroll one menu item.
1033 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1034 ShowDownArrow
= FALSE
;
1037 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1038 HighlightOptionIndex
++;
1044 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1047 // Restore link list order for orderedlist
1050 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1051 HiiValue
.Value
.u64
= 0;
1052 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1053 HiiValue
.Value
.u8
= ValueArray
[Index
];
1054 if (HiiValue
.Value
.u8
) {
1058 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1059 if (OneOfOption
== NULL
) {
1060 return EFI_NOT_FOUND
;
1063 RemoveEntryList (&OneOfOption
->Link
);
1064 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1068 gBS
->FreePool (HiiValueArray
);
1069 return EFI_DEVICE_ERROR
;
1077 case CHAR_CARRIAGE_RETURN
:
1079 // return the current selection
1083 Link
= GetFirstNode (&Question
->OptionListHead
);
1084 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1085 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1087 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1090 if (Index
> Question
->MaxContainers
) {
1094 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1097 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1100 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1101 gBS
->FreePool (HiiValueArray
);
1103 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1104 if (EFI_ERROR (Status
)) {
1106 // Input value is not valid, restore Question Value
1108 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1110 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1111 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1124 Wait for a key to be pressed by user.
1126 @param Key The key which is pressed by user.
1128 @retval EFI_SUCCESS The function always completed successfully.
1133 OUT EFI_INPUT_KEY
*Key
1139 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1140 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1141 } while (EFI_ERROR(Status
));