2 Implementation for handling user input from the User Interfaces.
4 Copyright (c) 2004 - 2009, 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.
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 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", (UINT8
) EditValue
);
474 if (MenuOption
->Sequence
== 0) {
475 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
476 } else if (MenuOption
->Sequence
== 1) {
477 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
480 QuestionValue
->Value
.u64
= EditValue
;
481 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
484 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
485 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
486 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
488 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
490 if (MenuOption
->Sequence
== 0) {
491 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
492 Column
= MenuOption
->OptCol
+ 1;
495 PrintStringAt (Column
, Row
, FormattedNumber
);
497 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
498 PrintChar (RIGHT_NUMERIC_DELIMITER
);
502 goto EnterCarriageReturn
;
507 goto EnterCarriageReturn
;
510 return EFI_DEVICE_ERROR
;
520 case CHAR_CARRIAGE_RETURN
:
522 // Store Edit value back to Question
524 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
525 switch (MenuOption
->Sequence
) {
527 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
531 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
535 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
541 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
542 switch (MenuOption
->Sequence
) {
544 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
548 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
552 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
562 QuestionValue
->Value
.u64
= EditValue
;
566 // Check to see if the Value is something reasonable against consistency limitations.
567 // If not, let's kick the error specified.
569 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
570 if (EFI_ERROR (Status
)) {
572 // Input value is not valid, restore Question Value
574 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
576 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
577 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
579 // NV flag is unnecessary for RTC type of Date/Time
581 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
594 // Remove a character
596 EditValue
= PreviousNumber
[Count
- 1];
597 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
600 PrintAt (Column
, Row
, L
" ");
607 if ((Key
.UnicodeChar
>= L
'0') && (Key
.UnicodeChar
<= L
'9')) {
608 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'0');
609 } else if ((Key
.UnicodeChar
>= L
'A') && (Key
.UnicodeChar
<= L
'F')) {
610 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'A' + 0x0A);
611 } else if ((Key
.UnicodeChar
>= L
'a') && (Key
.UnicodeChar
<= L
'f')) {
612 Digital
= (UINT8
) (Key
.UnicodeChar
- L
'a' + 0x0A);
614 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
618 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
619 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
625 // If Count exceed input width, there is no way more is valid
627 if (Count
>= InputWidth
) {
631 // Someone typed something valid!
635 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
637 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
643 EditValue
= Key
.UnicodeChar
- L
'0';
647 if (EditValue
> Maximum
) {
648 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
649 ASSERT (Count
< sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0]));
650 EditValue
= PreviousNumber
[Count
];
653 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
657 ASSERT (Count
< (sizeof (PreviousNumber
) / sizeof (PreviousNumber
[0])));
658 PreviousNumber
[Count
] = EditValue
;
660 PrintCharAt (Column
, Row
, Key
.UnicodeChar
);
671 Get selection for OneOf and OrderedList (Left/Right will be ignored).
673 @param Selection Pointer to current selection.
674 @param MenuOption Pointer to the current input menu.
676 @retval EFI_SUCCESS If Option input is processed successfully
677 @retval EFI_DEVICE_ERROR If operation fails
681 GetSelectionInputPopUp (
682 IN UI_MENU_SELECTION
*Selection
,
683 IN UI_MENU_OPTION
*MenuOption
690 CHAR16
*TempStringPtr
;
692 UINTN TopOptionIndex
;
693 UINTN HighlightOptionIndex
;
698 UINTN PopUpMenuLines
;
699 UINTN MenuLinesInView
;
702 INT32 SavedAttribute
;
703 BOOLEAN ShowDownArrow
;
705 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
;
721 CurrentOption
= NULL
;
722 ShowDownArrow
= FALSE
;
725 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
728 Question
= MenuOption
->ThisTag
;
729 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
730 ValueArray
= Question
->BufferValue
;
731 ValueType
= Question
->ValueType
;
738 // Calculate Option count
741 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
742 if (GetArrayData (ValueArray
, ValueType
, Index
) == 0) {
750 Link
= GetFirstNode (&Question
->OptionListHead
);
751 while (!IsNull (&Question
->OptionListHead
, Link
)) {
752 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
756 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
761 // Prepare HiiValue array
763 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
764 ASSERT (HiiValueArray
!= NULL
);
765 Link
= GetFirstNode (&Question
->OptionListHead
);
766 for (Index
= 0; Index
< OptionCount
; Index
++) {
768 HiiValueArray
[Index
].Type
= ValueType
;
769 HiiValueArray
[Index
].Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
771 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
772 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
773 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
778 // Move Suppressed Option to list tail
781 for (Index
= 0; Index
< OptionCount
; Index
++) {
782 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
783 if (OneOfOption
== NULL
) {
784 return EFI_NOT_FOUND
;
787 RemoveEntryList (&OneOfOption
->Link
);
789 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
790 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
792 // This option is suppressed, insert to tail
794 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
799 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
806 // Get the number of one of options present and its size
809 HighlightOptionIndex
= 0;
810 Link
= GetFirstNode (&Question
->OptionListHead
);
811 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
812 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
814 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
815 if (StrLen (StringPtr
) > PopUpWidth
) {
816 PopUpWidth
= StrLen (StringPtr
);
818 FreePool (StringPtr
);
820 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
822 // Find current selected Option for OneOf
824 HighlightOptionIndex
= Index
;
827 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
831 // Perform popup menu initialization.
833 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
835 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
836 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
838 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
839 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
842 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
843 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
844 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
845 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
847 MenuLinesInView
= Bottom
- Top
- 1;
848 if (MenuLinesInView
>= PopUpMenuLines
) {
849 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
850 Bottom
= Top
+ PopUpMenuLines
+ 1;
852 ShowDownArrow
= TRUE
;
855 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
856 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
863 // Clear that portion of the screen
865 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
868 // Draw "One of" pop-up menu
870 Character
= BOXDRAW_DOWN_RIGHT
;
871 PrintCharAt (Start
, Top
, Character
);
872 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
873 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
874 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
876 Character
= BOXDRAW_HORIZONTAL
;
879 PrintChar (Character
);
882 Character
= BOXDRAW_DOWN_LEFT
;
883 PrintChar (Character
);
884 Character
= BOXDRAW_VERTICAL
;
885 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
886 PrintCharAt (Start
, Index
, Character
);
887 PrintCharAt (End
- 1, Index
, Character
);
891 // Move to top Option
893 Link
= GetFirstNode (&Question
->OptionListHead
);
894 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
895 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
899 // Display the One of options
902 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
903 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
904 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
906 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
908 // If the string occupies multiple lines, truncate it to fit in one line,
909 // and append a "..." for indication.
911 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
912 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
913 ASSERT ( TempStringPtr
!= NULL
);
914 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
915 FreePool (StringPtr
);
916 StringPtr
= TempStringPtr
;
917 StrCat (StringPtr
, L
"...");
920 if (Index
== HighlightOptionIndex
) {
922 // Highlight the selected one
924 CurrentOption
= OneOfOption
;
926 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
927 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
928 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
930 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
931 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
935 FreePool (StringPtr
);
938 Character
= BOXDRAW_UP_RIGHT
;
939 PrintCharAt (Start
, Bottom
, Character
);
940 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
941 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
942 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
944 Character
= BOXDRAW_HORIZONTAL
;
947 PrintChar (Character
);
950 Character
= BOXDRAW_UP_LEFT
;
951 PrintChar (Character
);
954 // Get User selection
956 Key
.UnicodeChar
= CHAR_NULL
;
957 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
958 Key
.ScanCode
= gDirection
;
963 Status
= WaitForKeyStroke (&Key
);
966 switch (Key
.UnicodeChar
) {
969 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
971 // Highlight reaches the top of the popup window, scroll one menu item.
974 ShowDownArrow
= TRUE
;
977 if (TopOptionIndex
== 0) {
981 if (HighlightOptionIndex
> 0) {
982 HighlightOptionIndex
--;
984 ASSERT (CurrentOption
!= NULL
);
985 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
992 // If an ordered list op-code, we will allow for a popup of +/- keys
993 // to create an ordered list of items
996 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
997 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
999 // Highlight reaches the bottom of the popup window, scroll one menu item.
1005 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1006 ShowDownArrow
= FALSE
;
1009 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1010 HighlightOptionIndex
++;
1012 ASSERT (CurrentOption
!= NULL
);
1013 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1019 switch (Key
.ScanCode
) {
1022 if (Key
.ScanCode
== SCAN_UP
) {
1023 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1025 // Highlight reaches the top of the popup window, scroll one menu item.
1028 ShowDownArrow
= TRUE
;
1031 if (TopOptionIndex
== 0) {
1032 ShowUpArrow
= FALSE
;
1035 if (HighlightOptionIndex
> 0) {
1036 HighlightOptionIndex
--;
1039 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1040 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1042 // Highlight reaches the bottom of the popup window, scroll one menu item.
1048 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1049 ShowDownArrow
= FALSE
;
1052 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1053 HighlightOptionIndex
++;
1059 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1062 // Restore link list order for orderedlist
1065 HiiValue
.Type
= ValueType
;
1066 HiiValue
.Value
.u64
= 0;
1067 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1068 HiiValue
.Value
.u64
= GetArrayData (ValueArray
, ValueType
, Index
);
1069 if (HiiValue
.Value
.u64
== 0) {
1073 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1074 if (OneOfOption
== NULL
) {
1075 return EFI_NOT_FOUND
;
1078 RemoveEntryList (&OneOfOption
->Link
);
1079 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1083 FreePool (HiiValueArray
);
1084 return EFI_DEVICE_ERROR
;
1092 case CHAR_CARRIAGE_RETURN
:
1094 // return the current selection
1098 Link
= GetFirstNode (&Question
->OptionListHead
);
1099 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1100 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1102 SetArrayData (ValueArray
, ValueType
, Index
, OneOfOption
->Value
.Value
.u64
);
1105 if (Index
> Question
->MaxContainers
) {
1109 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1112 ASSERT (CurrentOption
!= NULL
);
1113 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1116 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1117 FreePool (HiiValueArray
);
1119 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1120 if (EFI_ERROR (Status
)) {
1122 // Input value is not valid, restore Question Value
1124 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1126 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1127 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1140 Wait for a key to be pressed by user.
1142 @param Key The key which is pressed by user.
1144 @retval EFI_SUCCESS The function always completed successfully.
1149 OUT EFI_INPUT_KEY
*Key
1155 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1156 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1157 } while (EFI_ERROR(Status
));