2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2009, Intel Corporation. All rights reserved.<BR>
5 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.
19 Get string or password input from user.
21 @param MenuOption Pointer to the current input menu.
22 @param Prompt The prompt string shown on popup window.
23 @param StringPtr Destination for use input string.
25 @retval EFI_SUCCESS If string input is read successfully
26 @retval EFI_DEVICE_ERROR If operation fails
31 IN UI_MENU_OPTION
*MenuOption
,
43 CHAR16
*BufferedString
;
48 UINTN DimensionsWidth
;
49 UINTN DimensionsHeight
;
50 BOOLEAN CursorVisible
;
53 FORM_BROWSER_STATEMENT
*Question
;
56 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
57 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
59 NullCharacter
= CHAR_NULL
;
60 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
64 Question
= MenuOption
->ThisTag
;
65 Minimum
= (UINTN
) Question
->Minimum
;
66 Maximum
= (UINTN
) Question
->Maximum
;
68 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
74 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
77 if (ScreenSize
< (Maximum
+ 1)) {
78 ScreenSize
= Maximum
+ 1;
81 if ((ScreenSize
+ 2) > DimensionsWidth
) {
82 ScreenSize
= DimensionsWidth
- 2;
85 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
86 ASSERT (BufferedString
);
88 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
89 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
92 // Display prompt for string
94 CreateMultiStringPopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
96 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
98 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
99 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
102 Status
= WaitForKeyStroke (&Key
);
103 ASSERT_EFI_ERROR (Status
);
105 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
106 switch (Key
.UnicodeChar
) {
108 switch (Key
.ScanCode
) {
116 FreePool (TempString
);
117 FreePool (BufferedString
);
118 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
119 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
120 return EFI_DEVICE_ERROR
;
128 case CHAR_CARRIAGE_RETURN
:
129 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
131 FreePool (TempString
);
132 FreePool (BufferedString
);
133 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
134 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
138 // Simply create a popup to tell the user that they had typed in too few characters.
139 // To save code space, we can then treat this as an error and return back to the menu.
142 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
143 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
145 FreePool (TempString
);
146 FreePool (BufferedString
);
147 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
148 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
149 return EFI_DEVICE_ERROR
;
155 if (StringPtr
[0] != CHAR_NULL
) {
156 for (Index
= 0; StringPtr
[Index
] != CHAR_NULL
; Index
++) {
157 TempString
[Index
] = StringPtr
[Index
];
160 // Effectively truncate string by 1 character
162 TempString
[Index
- 1] = CHAR_NULL
;
163 StrCpy (StringPtr
, TempString
);
168 // If it is the beginning of the string, don't worry about checking maximum limits
170 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
171 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
172 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
173 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
174 KeyPad
[0] = Key
.UnicodeChar
;
175 KeyPad
[1] = CHAR_NULL
;
176 StrCat (StringPtr
, KeyPad
);
177 StrCat (TempString
, KeyPad
);
181 // If the width of the input string is now larger than the screen, we nee to
182 // adjust the index to start printing portions of the string
184 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
185 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
187 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
188 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
194 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
197 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
198 BufferedString
[Count
] = StringPtr
[Index
];
206 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
211 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
212 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
219 This routine reads a numeric value from the user input.
221 @param Selection Pointer to current selection.
222 @param MenuOption Pointer to the current input menu.
224 @retval EFI_SUCCESS If numerical input is read successfully
225 @retval EFI_DEVICE_ERROR If operation fails
230 IN UI_MENU_SELECTION
*Selection
,
231 IN UI_MENU_OPTION
*MenuOption
237 CHAR16 InputText
[MAX_NUMERIC_INPUT_WIDTH
];
238 CHAR16 FormattedNumber
[MAX_NUMERIC_INPUT_WIDTH
- 1];
239 UINT64 PreviousNumber
[MAX_NUMERIC_INPUT_WIDTH
- 3];
253 EFI_HII_VALUE
*QuestionValue
;
254 FORM_BROWSER_FORM
*Form
;
255 FORM_BROWSER_FORMSET
*FormSet
;
256 FORM_BROWSER_STATEMENT
*Question
;
258 Column
= MenuOption
->OptCol
;
259 Row
= MenuOption
->Row
;
260 PreviousNumber
[0] = 0;
265 FormSet
= Selection
->FormSet
;
266 Form
= Selection
->Form
;
267 Question
= MenuOption
->ThisTag
;
268 QuestionValue
= &Question
->HiiValue
;
269 Step
= Question
->Step
;
270 Minimum
= Question
->Minimum
;
271 Maximum
= Question
->Maximum
;
273 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
280 // Prepare Value to be edit
284 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
288 switch (MenuOption
->Sequence
) {
292 EditValue
= QuestionValue
->Value
.date
.Month
;
298 EditValue
= QuestionValue
->Value
.date
.Day
;
304 EditValue
= QuestionValue
->Value
.date
.Year
;
310 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
314 switch (MenuOption
->Sequence
) {
318 EditValue
= QuestionValue
->Value
.time
.Hour
;
324 EditValue
= QuestionValue
->Value
.time
.Minute
;
330 EditValue
= QuestionValue
->Value
.time
.Second
;
340 EraseLen
= gOptionBlockWidth
;
341 EditValue
= QuestionValue
->Value
.u64
;
343 Maximum
= (UINT64
) -1;
353 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
354 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
362 InputWidth
= Question
->StorageWidth
* 2;
364 switch (Question
->StorageWidth
) {
387 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
388 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
389 ASSERT (InputWidth
+ 2 < MAX_NUMERIC_INPUT_WIDTH
);
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", (UINT16
) EditValue
);
463 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
466 if (MenuOption
->Sequence
== 0) {
467 ASSERT (EraseLen
>= 2);
468 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
469 } else if (MenuOption
->Sequence
== 1) {
470 ASSERT (EraseLen
>= 1);
471 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
473 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
474 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", (UINT8
) EditValue
);
476 if (MenuOption
->Sequence
== 0) {
477 ASSERT (EraseLen
>= 2);
478 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
479 } else if (MenuOption
->Sequence
== 1) {
480 ASSERT (EraseLen
>= 1);
481 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
484 QuestionValue
->Value
.u64
= EditValue
;
485 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
488 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
489 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
490 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
492 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
494 if (MenuOption
->Sequence
== 0) {
495 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
496 Column
= MenuOption
->OptCol
+ 1;
499 PrintStringAt (Column
, Row
, FormattedNumber
);
501 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
502 PrintChar (RIGHT_NUMERIC_DELIMITER
);
506 goto EnterCarriageReturn
;
511 goto EnterCarriageReturn
;
514 return EFI_DEVICE_ERROR
;
524 case CHAR_CARRIAGE_RETURN
:
526 // Store Edit value back to Question
528 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
529 switch (MenuOption
->Sequence
) {
531 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
535 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
539 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
545 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
546 switch (MenuOption
->Sequence
) {
548 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
552 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
556 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
566 QuestionValue
->Value
.u64
= EditValue
;
570 // Check to see if the Value is something reasonable against consistency limitations.
571 // If not, let's kick the error specified.
573 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
574 if (EFI_ERROR (Status
)) {
576 // Input value is not valid, restore Question Value
578 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
580 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
581 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
583 // NV flag is unnecessary for RTC type of Date/Time
585 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
598 // Remove a character
600 EditValue
= PreviousNumber
[Count
- 1];
601 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
604 PrintAt (Column
, Row
, L
" ");
611 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
612 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
613 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
614 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
615 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
616 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
618 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
622 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
623 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
629 // If Count exceed input width, there is no way more is valid
631 if (Count
>= InputWidth
) {
635 // Someone typed something valid!
639 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
641 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
647 EditValue
= Key
.UnicodeChar
- L
'0';
651 if (EditValue
> Maximum
) {
652 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
653 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
654 EditValue
= PreviousNumber
[Count
];
657 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
661 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
662 PreviousNumber
[Count
] = EditValue
;
664 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
675 Get selection for OneOf and OrderedList (Left/Right will be ignored).
677 @param Selection Pointer to current selection.
678 @param MenuOption Pointer to the current input menu.
680 @retval EFI_SUCCESS If Option input is processed successfully
681 @retval EFI_DEVICE_ERROR If operation fails
685 GetSelectionInputPopUp (
686 IN UI_MENU_SELECTION
*Selection
,
687 IN UI_MENU_OPTION
*MenuOption
694 CHAR16
*TempStringPtr
;
696 UINTN TopOptionIndex
;
697 UINTN HighlightOptionIndex
;
702 UINTN PopUpMenuLines
;
703 UINTN MenuLinesInView
;
706 INT32 SavedAttribute
;
707 BOOLEAN ShowDownArrow
;
709 UINTN DimensionsWidth
;
714 EFI_HII_VALUE HiiValue
;
715 EFI_HII_VALUE
*HiiValueArray
;
717 QUESTION_OPTION
*OneOfOption
;
718 QUESTION_OPTION
*CurrentOption
;
719 FORM_BROWSER_STATEMENT
*Question
;
721 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
725 CurrentOption
= NULL
;
726 ShowDownArrow
= FALSE
;
729 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
732 Question
= MenuOption
->ThisTag
;
733 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
734 ValueArray
= Question
->BufferValue
;
735 ValueType
= Question
->ValueType
;
742 // Calculate Option count
745 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
746 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
754 Link
= GetFirstNode (&Question
->OptionListHead
);
755 while (!IsNull (&Question
->OptionListHead
, Link
)) {
756 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
760 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
765 // Prepare HiiValue array
767 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
768 ASSERT (HiiValueArray
!= NULL
);
769 Link
= GetFirstNode (&Question
->OptionListHead
);
770 for (Index
= 0; Index
< OptionCount
; Index
++) {
772 HiiValueArray
[Index
].Type
= ValueType
;
773 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
775 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
776 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
777 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
782 // Move Suppressed Option to list tail
785 for (Index
= 0; Index
< OptionCount
; Index
++) {
786 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
787 if (OneOfOption
== NULL
) {
788 return EFI_NOT_FOUND
;
791 RemoveEntryList (&OneOfOption
->Link
);
793 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
794 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
796 // This option is suppressed, insert to tail
798 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
803 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
810 // Get the number of one of options present and its size
813 HighlightOptionIndex
= 0;
814 Link
= GetFirstNode (&Question
->OptionListHead
);
815 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
816 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
818 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
819 if (StrLen (StringPtr
) > PopUpWidth
) {
820 PopUpWidth
= StrLen (StringPtr
);
822 FreePool (StringPtr
);
824 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
826 // Find current selected Option for OneOf
828 HighlightOptionIndex
= Index
;
831 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
835 // Perform popup menu initialization.
837 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
839 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
840 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
842 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
843 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
846 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
847 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
848 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
849 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
851 MenuLinesInView
= Bottom
- Top
- 1;
852 if (MenuLinesInView
>= PopUpMenuLines
) {
853 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
854 Bottom
= Top
+ PopUpMenuLines
+ 1;
856 ShowDownArrow
= TRUE
;
859 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
860 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
867 // Clear that portion of the screen
869 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
872 // Draw "One of" pop-up menu
874 Character
= BOXDRAW_DOWN_RIGHT
;
875 PrintCharAt (Start
, Top
, Character
);
876 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
877 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
878 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
880 Character
= BOXDRAW_HORIZONTAL
;
883 PrintChar (Character
);
886 Character
= BOXDRAW_DOWN_LEFT
;
887 PrintChar (Character
);
888 Character
= BOXDRAW_VERTICAL
;
889 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
890 PrintCharAt (Start
, Index
, Character
);
891 PrintCharAt (End
- 1, Index
, Character
);
895 // Move to top Option
897 Link
= GetFirstNode (&Question
->OptionListHead
);
898 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
899 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
903 // Display the One of options
906 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
907 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
908 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
910 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
912 // If the string occupies multiple lines, truncate it to fit in one line,
913 // and append a "..." for indication.
915 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
916 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
917 ASSERT ( TempStringPtr
!= NULL
);
918 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
919 FreePool (StringPtr
);
920 StringPtr
= TempStringPtr
;
921 StrCat (StringPtr
, L
"...");
924 if (Index
== HighlightOptionIndex
) {
926 // Highlight the selected one
928 CurrentOption
= OneOfOption
;
930 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
931 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
932 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
934 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
935 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
939 FreePool (StringPtr
);
942 Character
= BOXDRAW_UP_RIGHT
;
943 PrintCharAt (Start
, Bottom
, Character
);
944 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
945 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
946 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
948 Character
= BOXDRAW_HORIZONTAL
;
951 PrintChar (Character
);
954 Character
= BOXDRAW_UP_LEFT
;
955 PrintChar (Character
);
958 // Get User selection
960 Key
.UnicodeChar
= CHAR_NULL
;
961 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
962 Key
.ScanCode
= gDirection
;
967 Status
= WaitForKeyStroke (&Key
);
970 switch (Key
.UnicodeChar
) {
973 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
975 // Highlight reaches the top of the popup window, scroll one menu item.
978 ShowDownArrow
= TRUE
;
981 if (TopOptionIndex
== 0) {
985 if (HighlightOptionIndex
> 0) {
986 HighlightOptionIndex
--;
988 ASSERT (CurrentOption
!= NULL
);
989 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
996 // If an ordered list op-code, we will allow for a popup of +/- keys
997 // to create an ordered list of items
1000 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1001 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1003 // Highlight reaches the bottom of the popup window, scroll one menu item.
1009 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1010 ShowDownArrow
= FALSE
;
1013 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1014 HighlightOptionIndex
++;
1016 ASSERT (CurrentOption
!= NULL
);
1017 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1023 switch (Key
.ScanCode
) {
1026 if (Key
.ScanCode
== SCAN_UP
) {
1027 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1029 // Highlight reaches the top of the popup window, scroll one menu item.
1032 ShowDownArrow
= TRUE
;
1035 if (TopOptionIndex
== 0) {
1036 ShowUpArrow
= FALSE
;
1039 if (HighlightOptionIndex
> 0) {
1040 HighlightOptionIndex
--;
1043 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1044 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1046 // Highlight reaches the bottom of the popup window, scroll one menu item.
1052 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1053 ShowDownArrow
= FALSE
;
1056 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1057 HighlightOptionIndex
++;
1063 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1066 // Restore link list order for orderedlist
1069 HiiValue
.Type
= ValueType
;
1070 HiiValue
.Value
.u64
= 0;
1071 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1072 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1073 if (HiiValue
.Value
.u64
== 0) {
1077 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1078 if (OneOfOption
== NULL
) {
1079 return EFI_NOT_FOUND
;
1082 RemoveEntryList (&OneOfOption
->Link
);
1083 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1087 FreePool (HiiValueArray
);
1088 return EFI_DEVICE_ERROR
;
1096 case CHAR_CARRIAGE_RETURN
:
1098 // return the current selection
1102 Link
= GetFirstNode (&Question
->OptionListHead
);
1103 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1104 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1106 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1109 if (Index
> Question
->MaxContainers
) {
1113 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1116 ASSERT (CurrentOption
!= NULL
);
1117 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1120 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1121 FreePool (HiiValueArray
);
1123 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1124 if (EFI_ERROR (Status
)) {
1126 // Input value is not valid, restore Question Value
1128 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1130 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1131 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1144 Wait for a key to be pressed by user.
1146 @param Key The key which is pressed by user.
1148 @retval EFI_SUCCESS The function always completed successfully.
1153 OUT EFI_INPUT_KEY
*Key
1159 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1160 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1161 } while (EFI_ERROR(Status
));