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
[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
< 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 (!IsHexDigit (&Digital
, Key
.UnicodeChar
)) {
609 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
613 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
614 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
620 // If Count exceed input width, there is no way more is valid
622 if (Count
>= InputWidth
) {
626 // Someone typed something valid!
630 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
632 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
638 EditValue
= Key
.UnicodeChar
- L
'0';
642 if (EditValue
> Maximum
) {
643 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
644 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
645 EditValue
= PreviousNumber
[Count
];
648 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
652 PreviousNumber
[Count
] = EditValue
;
654 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
665 Get selection for OneOf and OrderedList (Left/Right will be ignored).
667 @param Selection Pointer to current selection.
668 @param MenuOption Pointer to the current input menu.
670 @retval EFI_SUCCESS If Option input is processed successfully
671 @retval EFI_DEVICE_ERROR If operation fails
675 GetSelectionInputPopUp (
676 IN UI_MENU_SELECTION
*Selection
,
677 IN UI_MENU_OPTION
*MenuOption
684 CHAR16
*TempStringPtr
;
686 UINTN TopOptionIndex
;
687 UINTN HighlightOptionIndex
;
692 UINTN PopUpMenuLines
;
693 UINTN MenuLinesInView
;
696 INT32 SavedAttribute
;
697 BOOLEAN ShowDownArrow
;
699 UINTN DimensionsWidth
;
703 EFI_HII_VALUE HiiValue
;
704 EFI_HII_VALUE
*HiiValueArray
;
706 QUESTION_OPTION
*OneOfOption
;
707 QUESTION_OPTION
*CurrentOption
;
708 FORM_BROWSER_STATEMENT
*Question
;
710 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
713 CurrentOption
= NULL
;
714 ShowDownArrow
= FALSE
;
717 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
720 Question
= MenuOption
->ThisTag
;
721 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
722 ValueArray
= Question
->BufferValue
;
729 // Calculate Option count
732 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
733 if (ValueArray
[Index
] == 0) {
741 Link
= GetFirstNode (&Question
->OptionListHead
);
742 while (!IsNull (&Question
->OptionListHead
, Link
)) {
743 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
747 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
752 // Prepare HiiValue array
754 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
755 ASSERT (HiiValueArray
!= NULL
);
756 Link
= GetFirstNode (&Question
->OptionListHead
);
757 for (Index
= 0; Index
< OptionCount
; Index
++) {
759 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
760 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
762 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
763 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
764 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
769 // Move Suppressed Option to list tail
772 for (Index
= 0; Index
< OptionCount
; Index
++) {
773 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
774 if (OneOfOption
== NULL
) {
775 return EFI_NOT_FOUND
;
778 RemoveEntryList (&OneOfOption
->Link
);
780 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
781 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
783 // This option is suppressed, insert to tail
785 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
790 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
797 // Get the number of one of options present and its size
800 HighlightOptionIndex
= 0;
801 Link
= GetFirstNode (&Question
->OptionListHead
);
802 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
803 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
805 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
806 if (StrLen (StringPtr
) > PopUpWidth
) {
807 PopUpWidth
= StrLen (StringPtr
);
809 FreePool (StringPtr
);
811 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
813 // Find current selected Option for OneOf
815 HighlightOptionIndex
= Index
;
818 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
822 // Perform popup menu initialization.
824 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
826 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
827 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
829 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
830 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
833 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
834 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
835 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
836 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
838 MenuLinesInView
= Bottom
- Top
- 1;
839 if (MenuLinesInView
>= PopUpMenuLines
) {
840 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
841 Bottom
= Top
+ PopUpMenuLines
+ 1;
843 ShowDownArrow
= TRUE
;
846 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
847 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
854 // Clear that portion of the screen
856 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
859 // Draw "One of" pop-up menu
861 Character
= BOXDRAW_DOWN_RIGHT
;
862 PrintCharAt (Start
, Top
, Character
);
863 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
864 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
865 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
867 Character
= BOXDRAW_HORIZONTAL
;
870 PrintChar (Character
);
873 Character
= BOXDRAW_DOWN_LEFT
;
874 PrintChar (Character
);
875 Character
= BOXDRAW_VERTICAL
;
876 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
877 PrintCharAt (Start
, Index
, Character
);
878 PrintCharAt (End
- 1, Index
, Character
);
882 // Move to top Option
884 Link
= GetFirstNode (&Question
->OptionListHead
);
885 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
886 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
890 // Display the One of options
893 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
894 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
895 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
897 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
899 // If the string occupies multiple lines, truncate it to fit in one line,
900 // and append a "..." for indication.
902 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
903 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
904 ASSERT ( TempStringPtr
!= NULL
);
905 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
906 FreePool (StringPtr
);
907 StringPtr
= TempStringPtr
;
908 StrCat (StringPtr
, L
"...");
911 if (Index
== HighlightOptionIndex
) {
913 // Highlight the selected one
915 CurrentOption
= OneOfOption
;
917 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
918 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
919 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
921 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
922 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
926 FreePool (StringPtr
);
929 Character
= BOXDRAW_UP_RIGHT
;
930 PrintCharAt (Start
, Bottom
, Character
);
931 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
932 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
933 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
935 Character
= BOXDRAW_HORIZONTAL
;
938 PrintChar (Character
);
941 Character
= BOXDRAW_UP_LEFT
;
942 PrintChar (Character
);
945 // Get User selection
947 Key
.UnicodeChar
= CHAR_NULL
;
948 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
949 Key
.ScanCode
= gDirection
;
954 Status
= WaitForKeyStroke (&Key
);
957 switch (Key
.UnicodeChar
) {
960 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
962 // Highlight reaches the top of the popup window, scroll one menu item.
965 ShowDownArrow
= TRUE
;
968 if (TopOptionIndex
== 0) {
972 if (HighlightOptionIndex
> 0) {
973 HighlightOptionIndex
--;
975 ASSERT (CurrentOption
!= NULL
);
976 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
983 // If an ordered list op-code, we will allow for a popup of +/- keys
984 // to create an ordered list of items
987 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
988 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
990 // Highlight reaches the bottom of the popup window, scroll one menu item.
996 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
997 ShowDownArrow
= FALSE
;
1000 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1001 HighlightOptionIndex
++;
1003 ASSERT (CurrentOption
!= NULL
);
1004 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1010 switch (Key
.ScanCode
) {
1013 if (Key
.ScanCode
== SCAN_UP
) {
1014 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1016 // Highlight reaches the top of the popup window, scroll one menu item.
1019 ShowDownArrow
= TRUE
;
1022 if (TopOptionIndex
== 0) {
1023 ShowUpArrow
= FALSE
;
1026 if (HighlightOptionIndex
> 0) {
1027 HighlightOptionIndex
--;
1030 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1031 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1033 // Highlight reaches the bottom of the popup window, scroll one menu item.
1039 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1040 ShowDownArrow
= FALSE
;
1043 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1044 HighlightOptionIndex
++;
1050 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1053 // Restore link list order for orderedlist
1056 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1057 HiiValue
.Value
.u64
= 0;
1058 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1059 HiiValue
.Value
.u8
= ValueArray
[Index
];
1060 if (HiiValue
.Value
.u8
) {
1064 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1065 if (OneOfOption
== NULL
) {
1066 return EFI_NOT_FOUND
;
1069 RemoveEntryList (&OneOfOption
->Link
);
1070 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1074 FreePool (HiiValueArray
);
1075 return EFI_DEVICE_ERROR
;
1083 case CHAR_CARRIAGE_RETURN
:
1085 // return the current selection
1089 Link
= GetFirstNode (&Question
->OptionListHead
);
1090 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1091 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1093 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1096 if (Index
> Question
->MaxContainers
) {
1100 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1103 ASSERT (CurrentOption
!= NULL
);
1104 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1107 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1108 FreePool (HiiValueArray
);
1110 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1111 if (EFI_ERROR (Status
)) {
1113 // Input value is not valid, restore Question Value
1115 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1117 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1118 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1131 Wait for a key to be pressed by user.
1133 @param Key The key which is pressed by user.
1135 @retval EFI_SUCCESS The function always completed successfully.
1140 OUT EFI_INPUT_KEY
*Key
1146 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1147 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1148 } while (EFI_ERROR(Status
));