3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
18 Implementation for handling user input from the User Interface
30 Get string or password input from user.
32 @param MenuOption Pointer to the current input menu.
33 @param Prompt The prompt string shown on popup window.
34 @param StringPtr Destination for use input string.
36 @retval EFI_SUCCESS If string input is read successfully
37 @retval EFI_DEVICE_ERROR If operation fails
42 IN UI_MENU_OPTION
*MenuOption
,
54 CHAR16
*BufferedString
;
59 UINTN DimensionsWidth
;
60 UINTN DimensionsHeight
;
61 BOOLEAN CursorVisible
;
64 FORM_BROWSER_STATEMENT
*Question
;
67 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
68 DimensionsHeight
= gScreenDimensions
.BottomRow
- gScreenDimensions
.TopRow
;
70 NullCharacter
= CHAR_NULL
;
71 ScreenSize
= GetStringWidth (Prompt
) / sizeof (CHAR16
);
75 Question
= MenuOption
->ThisTag
;
76 Minimum
= (UINTN
) Question
->Minimum
;
77 Maximum
= (UINTN
) Question
->Maximum
;
79 if (Question
->Operand
== EFI_IFR_PASSWORD_OP
) {
85 TempString
= AllocateZeroPool ((Maximum
+ 1)* sizeof (CHAR16
));
88 if (ScreenSize
< (Maximum
+ 1)) {
89 ScreenSize
= Maximum
+ 1;
92 if ((ScreenSize
+ 2) > DimensionsWidth
) {
93 ScreenSize
= DimensionsWidth
- 2;
96 BufferedString
= AllocateZeroPool (ScreenSize
* 2);
97 ASSERT (BufferedString
);
99 Start
= (DimensionsWidth
- ScreenSize
- 2) / 2 + gScreenDimensions
.LeftColumn
+ 1;
100 Top
= ((DimensionsHeight
- 6) / 2) + gScreenDimensions
.TopRow
- 1;
103 // Display prompt for string
105 CreatePopUp (ScreenSize
, 4, &NullCharacter
, Prompt
, Space
, &NullCharacter
);
107 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
109 CursorVisible
= gST
->ConOut
->Mode
->CursorVisible
;
110 gST
->ConOut
->EnableCursor (gST
->ConOut
, TRUE
);
113 Status
= WaitForKeyStroke (&Key
);
114 ASSERT_EFI_ERROR (Status
);
116 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_BLACK
, EFI_LIGHTGRAY
));
117 switch (Key
.UnicodeChar
) {
119 switch (Key
.ScanCode
) {
127 gBS
->FreePool (TempString
);
128 gBS
->FreePool (BufferedString
);
129 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
130 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
131 return EFI_DEVICE_ERROR
;
139 case CHAR_CARRIAGE_RETURN
:
140 if (GetStringWidth (StringPtr
) >= ((Minimum
+ 1) * sizeof (CHAR16
))) {
142 gBS
->FreePool (TempString
);
143 gBS
->FreePool (BufferedString
);
144 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
145 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
149 // Simply create a popup to tell the user that they had typed in too few characters.
150 // To save code space, we can then treat this as an error and return back to the menu.
153 CreateDialog (4, TRUE
, 0, NULL
, &Key
, &NullCharacter
, gMiniString
, gPressEnter
, &NullCharacter
);
154 } while (Key
.UnicodeChar
!= CHAR_CARRIAGE_RETURN
);
156 gBS
->FreePool (TempString
);
157 gBS
->FreePool (BufferedString
);
158 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
159 gST
->ConOut
->EnableCursor (gST
->ConOut
, CursorVisible
);
160 return EFI_DEVICE_ERROR
;
166 if (StringPtr
[0] != CHAR_NULL
) {
167 for (Index
= 0; StringPtr
[Index
] != CHAR_NULL
; Index
++) {
168 TempString
[Index
] = StringPtr
[Index
];
171 // Effectively truncate string by 1 character
173 TempString
[Index
- 1] = CHAR_NULL
;
174 StrCpy (StringPtr
, TempString
);
179 // If it is the beginning of the string, don't worry about checking maximum limits
181 if ((StringPtr
[0] == CHAR_NULL
) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
182 StrnCpy (StringPtr
, &Key
.UnicodeChar
, 1);
183 StrnCpy (TempString
, &Key
.UnicodeChar
, 1);
184 } else if ((GetStringWidth (StringPtr
) < ((Maximum
+ 1) * sizeof (CHAR16
))) && (Key
.UnicodeChar
!= CHAR_BACKSPACE
)) {
185 KeyPad
[0] = Key
.UnicodeChar
;
186 KeyPad
[1] = CHAR_NULL
;
187 StrCat (StringPtr
, KeyPad
);
188 StrCat (TempString
, KeyPad
);
192 // If the width of the input string is now larger than the screen, we nee to
193 // adjust the index to start printing portions of the string
195 SetUnicodeMem (BufferedString
, ScreenSize
- 1, L
' ');
196 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
198 if ((GetStringWidth (StringPtr
) / 2) > (DimensionsWidth
- 2)) {
199 Index
= (GetStringWidth (StringPtr
) / 2) - DimensionsWidth
+ 2;
205 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ 1, Top
+ 3);
208 for (Count
= 0; Index
+ 1 < GetStringWidth (StringPtr
) / 2; Index
++, Count
++) {
209 BufferedString
[Count
] = StringPtr
[Index
];
217 PrintStringAt (Start
+ 1, Top
+ 3, BufferedString
);
222 gST
->ConOut
->SetAttribute (gST
->ConOut
, EFI_TEXT_ATTR (EFI_LIGHTGRAY
, EFI_BLACK
));
223 gST
->ConOut
->SetCursorPosition (gST
->ConOut
, Start
+ GetStringWidth (StringPtr
) / 2, Top
+ 3);
230 This routine reads a numeric value from the user input.
232 @param Selection Pointer to current selection.
233 @param MenuOption Pointer to the current input menu.
235 @retval EFI_SUCCESS If numerical input is read successfully
236 @retval EFI_DEVICE_ERROR If operation fails
241 IN UI_MENU_SELECTION
*Selection
,
242 IN UI_MENU_OPTION
*MenuOption
248 CHAR16 InputText
[23];
249 CHAR16 FormattedNumber
[22];
250 UINT64 PreviousNumber
[20];
264 EFI_HII_VALUE
*QuestionValue
;
265 FORM_BROWSER_FORM
*Form
;
266 FORM_BROWSER_FORMSET
*FormSet
;
267 FORM_BROWSER_STATEMENT
*Question
;
269 Column
= MenuOption
->OptCol
;
270 Row
= MenuOption
->Row
;
271 PreviousNumber
[0] = 0;
276 FormSet
= Selection
->FormSet
;
277 Form
= Selection
->Form
;
278 Question
= MenuOption
->ThisTag
;
279 QuestionValue
= &Question
->HiiValue
;
280 Step
= Question
->Step
;
281 Minimum
= Question
->Minimum
;
282 Maximum
= Question
->Maximum
;
284 if ((Question
->Operand
== EFI_IFR_DATE_OP
) || (Question
->Operand
== EFI_IFR_TIME_OP
)) {
291 // Prepare Value to be edit
295 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
299 switch (MenuOption
->Sequence
) {
303 EditValue
= QuestionValue
->Value
.date
.Month
;
309 EditValue
= QuestionValue
->Value
.date
.Day
;
315 EditValue
= QuestionValue
->Value
.date
.Year
;
321 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
325 switch (MenuOption
->Sequence
) {
329 EditValue
= QuestionValue
->Value
.time
.Hour
;
335 EditValue
= QuestionValue
->Value
.time
.Minute
;
341 EditValue
= QuestionValue
->Value
.time
.Second
;
351 EraseLen
= gOptionBlockWidth
;
352 EditValue
= QuestionValue
->Value
.u64
;
354 Maximum
= (UINT64
) -1;
364 if ((Question
->Operand
== EFI_IFR_NUMERIC_OP
) &&
365 ((Question
->Flags
& EFI_IFR_DISPLAY
) == EFI_IFR_DISPLAY_UINT_HEX
)) {
373 InputWidth
= Question
->StorageWidth
* 2;
375 switch (Question
->StorageWidth
) {
398 InputText
[0] = LEFT_NUMERIC_DELIMITER
;
399 SetUnicodeMem (InputText
+ 1, InputWidth
, L
' ');
400 InputText
[InputWidth
+ 1] = RIGHT_NUMERIC_DELIMITER
;
401 InputText
[InputWidth
+ 2] = L
'\0';
403 PrintAt (Column
, Row
, InputText
);
408 // First time we enter this handler, we need to check to see if
409 // we were passed an increment or decrement directive
412 Key
.UnicodeChar
= CHAR_NULL
;
413 if (gDirection
!= 0) {
414 Key
.ScanCode
= gDirection
;
419 Status
= WaitForKeyStroke (&Key
);
422 switch (Key
.UnicodeChar
) {
426 if (Key
.UnicodeChar
== '+') {
427 Key
.ScanCode
= SCAN_RIGHT
;
429 Key
.ScanCode
= SCAN_LEFT
;
431 Key
.UnicodeChar
= CHAR_NULL
;
435 switch (Key
.ScanCode
) {
440 // By setting this value, we will return back to the caller.
441 // We need to do this since an auto-refresh will destroy the adjustment
442 // based on what the real-time-clock is showing. So we always commit
443 // upon changing the value.
445 gDirection
= SCAN_DOWN
;
449 if (Key
.ScanCode
== SCAN_LEFT
) {
450 if (EditValue
> Step
) {
451 EditValue
= EditValue
- Step
;
455 } else if (Key
.ScanCode
== SCAN_RIGHT
) {
456 EditValue
= EditValue
+ Step
;
457 if (EditValue
> Maximum
) {
462 ZeroMem (FormattedNumber
, 21 * sizeof (CHAR16
));
463 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
464 if (MenuOption
->Sequence
== 2) {
468 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%04d", EditValue
);
473 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
476 if (MenuOption
->Sequence
== 0) {
477 FormattedNumber
[EraseLen
- 2] = DATE_SEPARATOR
;
478 } else if (MenuOption
->Sequence
== 1) {
479 FormattedNumber
[EraseLen
- 1] = DATE_SEPARATOR
;
481 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
482 UnicodeSPrint (FormattedNumber
, 21 * sizeof (CHAR16
), L
"%02d", EditValue
);
484 if (MenuOption
->Sequence
== 0) {
485 FormattedNumber
[EraseLen
- 2] = TIME_SEPARATOR
;
486 } else if (MenuOption
->Sequence
== 1) {
487 FormattedNumber
[EraseLen
- 1] = TIME_SEPARATOR
;
490 QuestionValue
->Value
.u64
= EditValue
;
491 PrintFormattedNumber (Question
, FormattedNumber
, 21 * sizeof (CHAR16
));
494 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT
| FIELD_BACKGROUND
);
495 for (Loop
= 0; Loop
< EraseLen
; Loop
++) {
496 PrintAt (MenuOption
->OptCol
+ Loop
, MenuOption
->Row
, L
" ");
498 gST
->ConOut
->SetAttribute (gST
->ConOut
, FIELD_TEXT_HIGHLIGHT
| FIELD_BACKGROUND_HIGHLIGHT
);
500 if (MenuOption
->Sequence
== 0) {
501 PrintCharAt (MenuOption
->OptCol
, Row
, LEFT_NUMERIC_DELIMITER
);
502 Column
= MenuOption
->OptCol
+ 1;
505 PrintStringAt (Column
, Row
, FormattedNumber
);
507 if (!DateOrTime
|| MenuOption
->Sequence
== 2) {
508 PrintChar (RIGHT_NUMERIC_DELIMITER
);
515 goto EnterCarriageReturn
;
518 return EFI_DEVICE_ERROR
;
528 case CHAR_CARRIAGE_RETURN
:
530 // Store Edit value back to Question
532 if (Question
->Operand
== EFI_IFR_DATE_OP
) {
533 switch (MenuOption
->Sequence
) {
535 QuestionValue
->Value
.date
.Month
= (UINT8
) EditValue
;
539 QuestionValue
->Value
.date
.Day
= (UINT8
) EditValue
;
543 QuestionValue
->Value
.date
.Year
= (UINT16
) EditValue
;
549 } else if (Question
->Operand
== EFI_IFR_TIME_OP
) {
550 switch (MenuOption
->Sequence
) {
552 QuestionValue
->Value
.time
.Hour
= (UINT8
) EditValue
;
556 QuestionValue
->Value
.time
.Minute
= (UINT8
) EditValue
;
560 QuestionValue
->Value
.time
.Second
= (UINT8
) EditValue
;
570 QuestionValue
->Value
.u64
= EditValue
;
574 // Check to see if the Value is something reasonable against consistency limitations.
575 // If not, let's kick the error specified.
577 Status
= ValidateQuestion (FormSet
, Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
578 if (EFI_ERROR (Status
)) {
580 // Input value is not valid, restore Question Value
582 GetQuestionValue (FormSet
, Form
, Question
, TRUE
);
584 SetQuestionValue (FormSet
, Form
, Question
, TRUE
);
585 if (!DateOrTime
|| (Question
->Storage
!= NULL
)) {
587 // NV flag is unnecessary for RTC type of Date/Time
589 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
602 // Remove a character
604 EditValue
= PreviousNumber
[Count
- 1];
605 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
608 PrintAt (Column
, Row
, L
" ");
615 if (!R8_IsHexDigit (&Digital
, Key
.UnicodeChar
)) {
616 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
620 if (Key
.UnicodeChar
> L
'9' || Key
.UnicodeChar
< L
'0') {
621 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
627 // If Count exceed input width, there is no way more is valid
629 if (Count
>= InputWidth
) {
633 // Someone typed something valid!
637 EditValue
= LShiftU64 (EditValue
, 4) + Digital
;
639 EditValue
= MultU64x32 (EditValue
, 10) + (Key
.UnicodeChar
- L
'0');
645 EditValue
= Key
.UnicodeChar
- L
'0';
649 if (EditValue
> Maximum
) {
650 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, TRUE
);
651 EditValue
= PreviousNumber
[Count
];
654 UpdateStatusBar (INPUT_ERROR
, Question
->QuestionFlags
, FALSE
);
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
;
709 EFI_HII_VALUE HiiValue
;
710 EFI_HII_VALUE
*HiiValueArray
;
712 QUESTION_OPTION
*OneOfOption
;
713 QUESTION_OPTION
*CurrentOption
;
714 FORM_BROWSER_STATEMENT
*Question
;
716 DimensionsWidth
= gScreenDimensions
.RightColumn
- gScreenDimensions
.LeftColumn
;
719 CurrentOption
= NULL
;
720 ShowDownArrow
= FALSE
;
723 StringPtr
= AllocateZeroPool ((gOptionBlockWidth
+ 1) * 2);
726 Question
= MenuOption
->ThisTag
;
727 if (Question
->Operand
== EFI_IFR_ORDERED_LIST_OP
) {
728 ValueArray
= Question
->BufferValue
;
735 // Calculate Option count
738 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
739 if (ValueArray
[Index
] == 0) {
747 Link
= GetFirstNode (&Question
->OptionListHead
);
748 while (!IsNull (&Question
->OptionListHead
, Link
)) {
749 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
753 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
758 // Prepare HiiValue array
760 HiiValueArray
= AllocateZeroPool (OptionCount
* sizeof (EFI_HII_VALUE
));
761 ASSERT (HiiValueArray
!= NULL
);
762 Link
= GetFirstNode (&Question
->OptionListHead
);
763 for (Index
= 0; Index
< OptionCount
; Index
++) {
765 HiiValueArray
[Index
].Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
766 HiiValueArray
[Index
].Value
.u8
= ValueArray
[Index
];
768 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
769 CopyMem (&HiiValueArray
[Index
], &OneOfOption
->Value
, sizeof (EFI_HII_VALUE
));
770 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
775 // Move Suppressed Option to list tail
778 for (Index
= 0; Index
< OptionCount
; Index
++) {
779 OneOfOption
= ValueToOption (Question
, &HiiValueArray
[OptionCount
- Index
- 1]);
780 if (OneOfOption
== NULL
) {
781 return EFI_NOT_FOUND
;
784 RemoveEntryList (&OneOfOption
->Link
);
786 if ((OneOfOption
->SuppressExpression
!= NULL
) &&
787 (OneOfOption
->SuppressExpression
->Result
.Value
.b
)) {
789 // This option is suppressed, insert to tail
791 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
796 InsertHeadList (&Question
->OptionListHead
, &OneOfOption
->Link
);
803 // Get the number of one of options present and its size
806 HighlightOptionIndex
= 0;
807 Link
= GetFirstNode (&Question
->OptionListHead
);
808 for (Index
= 0; Index
< PopUpMenuLines
; Index
++) {
809 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
811 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
812 if (StrLen (StringPtr
) > PopUpWidth
) {
813 PopUpWidth
= StrLen (StringPtr
);
815 gBS
->FreePool (StringPtr
);
817 if (!OrderedList
&& CompareHiiValue (&Question
->HiiValue
, &OneOfOption
->Value
, NULL
) == 0) {
819 // Find current selected Option for OneOf
821 HighlightOptionIndex
= Index
;
824 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
828 // Perform popup menu initialization.
830 PopUpWidth
= PopUpWidth
+ POPUP_PAD_SPACE_COUNT
;
832 SavedAttribute
= gST
->ConOut
->Mode
->Attribute
;
833 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
835 if ((PopUpWidth
+ POPUP_FRAME_WIDTH
) > DimensionsWidth
) {
836 PopUpWidth
= DimensionsWidth
- POPUP_FRAME_WIDTH
;
839 Start
= (DimensionsWidth
- PopUpWidth
- POPUP_FRAME_WIDTH
) / 2 + gScreenDimensions
.LeftColumn
;
840 End
= Start
+ PopUpWidth
+ POPUP_FRAME_WIDTH
;
841 Top
= gScreenDimensions
.TopRow
+ NONE_FRONT_PAGE_HEADER_HEIGHT
;
842 Bottom
= gScreenDimensions
.BottomRow
- STATUS_BAR_HEIGHT
- FOOTER_HEIGHT
- 1;
844 MenuLinesInView
= Bottom
- Top
- 1;
845 if (MenuLinesInView
>= PopUpMenuLines
) {
846 Top
= Top
+ (MenuLinesInView
- PopUpMenuLines
) / 2;
847 Bottom
= Top
+ PopUpMenuLines
+ 1;
849 ShowDownArrow
= TRUE
;
852 if (HighlightOptionIndex
> (MenuLinesInView
- 1)) {
853 TopOptionIndex
= HighlightOptionIndex
- MenuLinesInView
+ 1;
860 // Clear that portion of the screen
862 ClearLines (Start
, End
, Top
, Bottom
, POPUP_TEXT
| POPUP_BACKGROUND
);
865 // Draw "One of" pop-up menu
867 Character
= BOXDRAW_DOWN_RIGHT
;
868 PrintCharAt (Start
, Top
, Character
);
869 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
870 if ((ShowUpArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
871 Character
= GEOMETRICSHAPE_UP_TRIANGLE
;
873 Character
= BOXDRAW_HORIZONTAL
;
876 PrintChar (Character
);
879 Character
= BOXDRAW_DOWN_LEFT
;
880 PrintChar (Character
);
881 Character
= BOXDRAW_VERTICAL
;
882 for (Index
= Top
+ 1; Index
< Bottom
; Index
++) {
883 PrintCharAt (Start
, Index
, Character
);
884 PrintCharAt (End
- 1, Index
, Character
);
888 // Move to top Option
890 Link
= GetFirstNode (&Question
->OptionListHead
);
891 for (Index
= 0; Index
< TopOptionIndex
; Index
++) {
892 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
896 // Display the One of options
899 for (Index
= TopOptionIndex
; (Index
< PopUpMenuLines
) && (Index2
< Bottom
); Index
++) {
900 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
901 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
903 StringPtr
= GetToken (OneOfOption
->Text
, MenuOption
->Handle
);
905 // If the string occupies multiple lines, truncate it to fit in one line,
906 // and append a "..." for indication.
908 if (StrLen (StringPtr
) > (PopUpWidth
- 1)) {
909 TempStringPtr
= AllocateZeroPool (sizeof (CHAR16
) * (PopUpWidth
- 1));
910 ASSERT ( TempStringPtr
!= NULL
);
911 CopyMem (TempStringPtr
, StringPtr
, (sizeof (CHAR16
) * (PopUpWidth
- 5)));
912 gBS
->FreePool (StringPtr
);
913 StringPtr
= TempStringPtr
;
914 StrCat (StringPtr
, L
"...");
917 if (Index
== HighlightOptionIndex
) {
919 // Highlight the selected one
921 CurrentOption
= OneOfOption
;
923 gST
->ConOut
->SetAttribute (gST
->ConOut
, PICKLIST_HIGHLIGHT_TEXT
| PICKLIST_HIGHLIGHT_BACKGROUND
);
924 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
925 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
927 gST
->ConOut
->SetAttribute (gST
->ConOut
, POPUP_TEXT
| POPUP_BACKGROUND
);
928 PrintStringAt (Start
+ 2, Index2
, StringPtr
);
932 gBS
->FreePool (StringPtr
);
935 Character
= BOXDRAW_UP_RIGHT
;
936 PrintCharAt (Start
, Bottom
, Character
);
937 for (Index
= Start
; Index
+ 2 < End
; Index
++) {
938 if ((ShowDownArrow
) && ((Index
+ 1) == (Start
+ End
) / 2)) {
939 Character
= GEOMETRICSHAPE_DOWN_TRIANGLE
;
941 Character
= BOXDRAW_HORIZONTAL
;
944 PrintChar (Character
);
947 Character
= BOXDRAW_UP_LEFT
;
948 PrintChar (Character
);
951 // Get User selection
953 Key
.UnicodeChar
= CHAR_NULL
;
954 if ((gDirection
== SCAN_UP
) || (gDirection
== SCAN_DOWN
)) {
955 Key
.ScanCode
= gDirection
;
960 Status
= WaitForKeyStroke (&Key
);
963 switch (Key
.UnicodeChar
) {
966 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
968 // Highlight reaches the top of the popup window, scroll one menu item.
971 ShowDownArrow
= TRUE
;
974 if (TopOptionIndex
== 0) {
978 if (HighlightOptionIndex
> 0) {
979 HighlightOptionIndex
--;
981 SwapListEntries (CurrentOption
->Link
.BackLink
, &CurrentOption
->Link
);
988 // If an ordered list op-code, we will allow for a popup of +/- keys
989 // to create an ordered list of items
992 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
993 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
995 // Highlight reaches the bottom of the popup window, scroll one menu item.
1001 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1002 ShowDownArrow
= FALSE
;
1005 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1006 HighlightOptionIndex
++;
1008 SwapListEntries (&CurrentOption
->Link
, CurrentOption
->Link
.ForwardLink
);
1014 switch (Key
.ScanCode
) {
1017 if (Key
.ScanCode
== SCAN_UP
) {
1018 if ((TopOptionIndex
> 0) && (TopOptionIndex
== HighlightOptionIndex
)) {
1020 // Highlight reaches the top of the popup window, scroll one menu item.
1023 ShowDownArrow
= TRUE
;
1026 if (TopOptionIndex
== 0) {
1027 ShowUpArrow
= FALSE
;
1030 if (HighlightOptionIndex
> 0) {
1031 HighlightOptionIndex
--;
1034 if (((TopOptionIndex
+ MenuLinesInView
) < PopUpMenuLines
) &&
1035 (HighlightOptionIndex
== (TopOptionIndex
+ MenuLinesInView
- 1))) {
1037 // Highlight reaches the bottom of the popup window, scroll one menu item.
1043 if ((TopOptionIndex
+ MenuLinesInView
) == PopUpMenuLines
) {
1044 ShowDownArrow
= FALSE
;
1047 if (HighlightOptionIndex
< (PopUpMenuLines
- 1)) {
1048 HighlightOptionIndex
++;
1054 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1057 // Restore link list order for orderedlist
1060 HiiValue
.Type
= EFI_IFR_TYPE_NUM_SIZE_8
;
1061 HiiValue
.Value
.u64
= 0;
1062 for (Index
= 0; Index
< Question
->MaxContainers
; Index
++) {
1063 HiiValue
.Value
.u8
= ValueArray
[Index
];
1064 if (HiiValue
.Value
.u8
) {
1068 OneOfOption
= ValueToOption (Question
, &HiiValue
);
1069 if (OneOfOption
== NULL
) {
1070 return EFI_NOT_FOUND
;
1073 RemoveEntryList (&OneOfOption
->Link
);
1074 InsertTailList (&Question
->OptionListHead
, &OneOfOption
->Link
);
1078 gBS
->FreePool (HiiValueArray
);
1079 return EFI_DEVICE_ERROR
;
1087 case CHAR_CARRIAGE_RETURN
:
1089 // return the current selection
1093 Link
= GetFirstNode (&Question
->OptionListHead
);
1094 while (!IsNull (&Question
->OptionListHead
, Link
)) {
1095 OneOfOption
= QUESTION_OPTION_FROM_LINK (Link
);
1097 Question
->BufferValue
[Index
] = OneOfOption
->Value
.Value
.u8
;
1100 if (Index
> Question
->MaxContainers
) {
1104 Link
= GetNextNode (&Question
->OptionListHead
, Link
);
1107 CopyMem (&Question
->HiiValue
, &CurrentOption
->Value
, sizeof (EFI_HII_VALUE
));
1110 gST
->ConOut
->SetAttribute (gST
->ConOut
, SavedAttribute
);
1111 gBS
->FreePool (HiiValueArray
);
1113 Status
= ValidateQuestion (Selection
->FormSet
, Selection
->Form
, Question
, EFI_HII_EXPRESSION_INCONSISTENT_IF
);
1114 if (EFI_ERROR (Status
)) {
1116 // Input value is not valid, restore Question Value
1118 GetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1120 SetQuestionValue (Selection
->FormSet
, Selection
->Form
, Question
, TRUE
);
1121 UpdateStatusBar (NV_UPDATE_REQUIRED
, Question
->QuestionFlags
, TRUE
);
1135 OUT EFI_INPUT_KEY
*Key
1141 UiWaitForSingleEvent (gST
->ConIn
->WaitForKey
, 0, 0);
1142 Status
= gST
->ConIn
->ReadKeyStroke (gST
->ConIn
, Key
);
1143 } while (EFI_ERROR(Status
));