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
+ 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
;
710 EFI_HII_VALUE HiiValue
;
711 EFI_HII_VALUE
*HiiValueArray
;
713 QUESTION_OPTION
*OneOfOption
;
714 QUESTION_OPTION
*CurrentOption
;
715 FORM_BROWSER_STATEMENT
*Question
;
717 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
720 CurrentOption
= NULL
;
721 ShowDownArrow
= FALSE
;
724 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
727 Question
= MenuOption
->ThisTag
;
728 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
729 ValueArray
= Question
->BufferValue
;
736 // Calculate Option count
739 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
740 if (ValueArray
[Index
] == 0) {
748 Link
= GetFirstNode (&Question
->OptionListHead
);
749 while (!IsNull (&Question
->OptionListHead
, Link
)) {
750 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
754 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
759 // Prepare HiiValue array
761 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
762 ASSERT (HiiValueArray
!= NULL
);
763 Link
= GetFirstNode (&Question
->OptionListHead
);
764 for (Index
= 0; Index
< OptionCount
; Index
++) {
766 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
767 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
769 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
770 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
771 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
776 // Move Suppressed Option to list tail
779 for (Index
= 0; Index
< OptionCount
; Index
++) {
780 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
781 if (OneOfOption
== NULL
) {
782 return EFI_NOT_FOUND
;
785 RemoveEntryList (&OneOfOption
->Link
);
787 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
788 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
790 // This option is suppressed, insert to tail
792 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
797 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
804 // Get the number of one of options present and its size
807 HighlightOptionIndex
= 0;
808 Link
= GetFirstNode (&Question
->OptionListHead
);
809 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
810 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
812 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
813 if (StrLen (StringPtr
) > PopUpWidth
) {
814 PopUpWidth
= StrLen (StringPtr
);
816 FreePool (StringPtr
);
818 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
820 // Find current selected Option for OneOf
822 HighlightOptionIndex
= Index
;
825 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
829 // Perform popup menu initialization.
831 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
833 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
834 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
836 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
837 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
840 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
841 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
842 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
843 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
845 MenuLinesInView
= Bottom
- Top
- 1;
846 if (MenuLinesInView
>= PopUpMenuLines
) {
847 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
848 Bottom
= Top
+ PopUpMenuLines
+ 1;
850 ShowDownArrow
= TRUE
;
853 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
854 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
861 // Clear that portion of the screen
863 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
866 // Draw "One of" pop-up menu
868 Character
= BOXDRAW_DOWN_RIGHT
;
869 PrintCharAt (Start
, Top
, Character
);
870 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
871 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
872 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
874 Character
= BOXDRAW_HORIZONTAL
;
877 PrintChar (Character
);
880 Character
= BOXDRAW_DOWN_LEFT
;
881 PrintChar (Character
);
882 Character
= BOXDRAW_VERTICAL
;
883 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
884 PrintCharAt (Start
, Index
, Character
);
885 PrintCharAt (End
- 1, Index
, Character
);
889 // Move to top Option
891 Link
= GetFirstNode (&Question
->OptionListHead
);
892 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
893 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
897 // Display the One of options
900 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
901 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
902 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
904 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
906 // If the string occupies multiple lines, truncate it to fit in one line,
907 // and append a "..." for indication.
909 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
910 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
911 ASSERT ( TempStringPtr
!= NULL
);
912 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
913 FreePool (StringPtr
);
914 StringPtr
= TempStringPtr
;
915 StrCat (StringPtr
, L
"...");
918 if (Index
== HighlightOptionIndex
) {
920 // Highlight the selected one
922 CurrentOption
= OneOfOption
;
924 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
925 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
926 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
929 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
933 FreePool (StringPtr
);
936 Character
= BOXDRAW_UP_RIGHT
;
937 PrintCharAt (Start
, Bottom
, Character
);
938 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
939 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
940 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
942 Character
= BOXDRAW_HORIZONTAL
;
945 PrintChar (Character
);
948 Character
= BOXDRAW_UP_LEFT
;
949 PrintChar (Character
);
952 // Get User selection
954 Key
.UnicodeChar
= CHAR_NULL
;
955 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
956 Key
.ScanCode
= gDirection
;
961 Status
= WaitForKeyStroke (&Key
);
964 switch (Key
.UnicodeChar
) {
967 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
969 // Highlight reaches the top of the popup window, scroll one menu item.
972 ShowDownArrow
= TRUE
;
975 if (TopOptionIndex
== 0) {
979 if (HighlightOptionIndex
> 0) {
980 HighlightOptionIndex
--;
982 ASSERT (CurrentOption
!= NULL
);
983 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
990 // If an ordered list op-code, we will allow for a popup of +/- keys
991 // to create an ordered list of items
994 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
995 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
997 // Highlight reaches the bottom of the popup window, scroll one menu item.
1003 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1004 ShowDownArrow
= FALSE
;
1007 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1008 HighlightOptionIndex
++;
1010 ASSERT (CurrentOption
!= NULL
);
1011 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1017 switch (Key
.ScanCode
) {
1020 if (Key
.ScanCode
== SCAN_UP
) {
1021 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1023 // Highlight reaches the top of the popup window, scroll one menu item.
1026 ShowDownArrow
= TRUE
;
1029 if (TopOptionIndex
== 0) {
1030 ShowUpArrow
= FALSE
;
1033 if (HighlightOptionIndex
> 0) {
1034 HighlightOptionIndex
--;
1037 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1038 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1040 // Highlight reaches the bottom of the popup window, scroll one menu item.
1046 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1047 ShowDownArrow
= FALSE
;
1050 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1051 HighlightOptionIndex
++;
1057 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1060 // Restore link list order for orderedlist
1063 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1064 HiiValue
.Value
.u64
= 0;
1065 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1066 HiiValue
.Value
.u8
= ValueArray
[Index
];
1067 if (HiiValue
.Value
.u8
!= 0) {
1071 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1072 if (OneOfOption
== NULL
) {
1073 return EFI_NOT_FOUND
;
1076 RemoveEntryList (&OneOfOption
->Link
);
1077 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1081 FreePool (HiiValueArray
);
1082 return EFI_DEVICE_ERROR
;
1090 case CHAR_CARRIAGE_RETURN
:
1092 // return the current selection
1096 Link
= GetFirstNode (&Question
->OptionListHead
);
1097 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1098 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1100 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1103 if (Index
> Question
->MaxContainers
) {
1107 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1110 ASSERT (CurrentOption
!= NULL
);
1111 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1114 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1115 FreePool (HiiValueArray
);
1117 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1118 if (EFI_ERROR (Status
)) {
1120 // Input value is not valid, restore Question Value
1122 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1124 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1125 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1138 Wait for a key to be pressed by user.
1140 @param Key The key which is pressed by user.
1142 @retval EFI_SUCCESS The function always completed successfully.
1147 OUT EFI_INPUT_KEY
*Key
1153 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1154 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1155 } while (EFI_ERROR(Status
));