]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
UefiCpuPkg: Move AsmRelocateApLoopStart from Mpfuncs.nasm to AmdSev.nasm
[mirror_edk2.git] / MdeModulePkg / Universal / DisplayEngineDxe / InputHandler.c
1 /** @file
2 Implementation for handling user input from the User Interfaces.
3
4 Copyright (c) 2004 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "FormDisplay.h"
10
11 /**
12 Get maximum and minimum info from this opcode.
13
14 @param OpCode Pointer to the current input opcode.
15 @param Minimum The minimum size info for this opcode.
16 @param Maximum The maximum size info for this opcode.
17
18 **/
19 VOID
20 GetFieldFromOp (
21 IN EFI_IFR_OP_HEADER *OpCode,
22 OUT UINTN *Minimum,
23 OUT UINTN *Maximum
24 )
25 {
26 EFI_IFR_STRING *StringOp;
27 EFI_IFR_PASSWORD *PasswordOp;
28
29 if (OpCode->OpCode == EFI_IFR_STRING_OP) {
30 StringOp = (EFI_IFR_STRING *)OpCode;
31 *Minimum = StringOp->MinSize;
32 *Maximum = StringOp->MaxSize;
33 } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
34 PasswordOp = (EFI_IFR_PASSWORD *)OpCode;
35 *Minimum = PasswordOp->MinSize;
36 *Maximum = PasswordOp->MaxSize;
37 } else {
38 *Minimum = 0;
39 *Maximum = 0;
40 }
41 }
42
43 /**
44 Get string or password input from user.
45
46 @param MenuOption Pointer to the current input menu.
47 @param Prompt The prompt string shown on popup window.
48 @param StringPtr Old user input and destination for use input string.
49
50 @retval EFI_SUCCESS If string input is read successfully
51 @retval EFI_DEVICE_ERROR If operation fails
52
53 **/
54 EFI_STATUS
55 ReadString (
56 IN UI_MENU_OPTION *MenuOption,
57 IN CHAR16 *Prompt,
58 IN OUT CHAR16 *StringPtr
59 )
60 {
61 EFI_STATUS Status;
62 EFI_INPUT_KEY Key;
63 CHAR16 NullCharacter;
64 UINTN ScreenSize;
65 CHAR16 Space[2];
66 CHAR16 KeyPad[2];
67 CHAR16 *TempString;
68 CHAR16 *BufferedString;
69 UINTN Index;
70 UINTN Index2;
71 UINTN Count;
72 UINTN Start;
73 UINTN Top;
74 UINTN DimensionsWidth;
75 UINTN DimensionsHeight;
76 UINTN CurrentCursor;
77 BOOLEAN CursorVisible;
78 UINTN Minimum;
79 UINTN Maximum;
80 FORM_DISPLAY_ENGINE_STATEMENT *Question;
81 BOOLEAN IsPassword;
82 UINTN MaxLen;
83
84 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
85 DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
86
87 NullCharacter = CHAR_NULL;
88 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);
89 Space[0] = L' ';
90 Space[1] = CHAR_NULL;
91
92 Question = MenuOption->ThisTag;
93 GetFieldFromOp (Question->OpCode, &Minimum, &Maximum);
94
95 if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
96 IsPassword = TRUE;
97 } else {
98 IsPassword = FALSE;
99 }
100
101 MaxLen = Maximum + 1;
102 TempString = AllocateZeroPool (MaxLen * sizeof (CHAR16));
103 ASSERT (TempString);
104
105 if (ScreenSize < (Maximum + 1)) {
106 ScreenSize = Maximum + 1;
107 }
108
109 if ((ScreenSize + 2) > DimensionsWidth) {
110 ScreenSize = DimensionsWidth - 2;
111 }
112
113 BufferedString = AllocateZeroPool (ScreenSize * 2);
114 ASSERT (BufferedString);
115
116 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1;
117 Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1;
118
119 //
120 // Display prompt for string
121 //
122 // CreateDialog (NULL, "", Prompt, Space, "", NULL);
123 CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
124 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
125
126 CursorVisible = gST->ConOut->Mode->CursorVisible;
127 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
128
129 CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;
130 if (CurrentCursor != 0) {
131 //
132 // Show the string which has beed saved before.
133 //
134 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
135 PrintStringAt (Start + 1, Top + 3, BufferedString);
136
137 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
138 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
139 } else {
140 Index = 0;
141 }
142
143 if (IsPassword) {
144 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
145 }
146
147 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
148 BufferedString[Count] = StringPtr[Index];
149
150 if (IsPassword) {
151 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
152 }
153 }
154
155 if (!IsPassword) {
156 PrintStringAt (Start + 1, Top + 3, BufferedString);
157 }
158
159 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
160 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
161 }
162
163 do {
164 Status = WaitForKeyStroke (&Key);
165 ASSERT_EFI_ERROR (Status);
166
167 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
168 switch (Key.UnicodeChar) {
169 case CHAR_NULL:
170 switch (Key.ScanCode) {
171 case SCAN_LEFT:
172 if (CurrentCursor > 0) {
173 CurrentCursor--;
174 }
175
176 break;
177
178 case SCAN_RIGHT:
179 if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {
180 CurrentCursor++;
181 }
182
183 break;
184
185 case SCAN_ESC:
186 FreePool (TempString);
187 FreePool (BufferedString);
188 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
189 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
190 return EFI_DEVICE_ERROR;
191
192 case SCAN_DELETE:
193 for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) {
194 StringPtr[Index] = StringPtr[Index + 1];
195 PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL ? L'*' : StringPtr[Index]);
196 }
197
198 break;
199
200 default:
201 break;
202 }
203
204 break;
205
206 case CHAR_CARRIAGE_RETURN:
207 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
208 FreePool (TempString);
209 FreePool (BufferedString);
210 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
211 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
212 return EFI_SUCCESS;
213 } else {
214 //
215 // Simply create a popup to tell the user that they had typed in too few characters.
216 // To save code space, we can then treat this as an error and return back to the menu.
217 //
218 do {
219 CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL);
220 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
221
222 FreePool (TempString);
223 FreePool (BufferedString);
224 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
225 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
226 return EFI_DEVICE_ERROR;
227 }
228
229 case CHAR_BACKSPACE:
230 if ((StringPtr[0] != CHAR_NULL) && (CurrentCursor != 0)) {
231 for (Index = 0; Index < CurrentCursor - 1; Index++) {
232 TempString[Index] = StringPtr[Index];
233 }
234
235 Count = GetStringWidth (StringPtr) / 2 - 1;
236 if (Count >= CurrentCursor) {
237 for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
238 TempString[Index] = StringPtr[Index2];
239 }
240
241 TempString[Index] = CHAR_NULL;
242 }
243
244 //
245 // Effectively truncate string by 1 character
246 //
247 StrCpyS (StringPtr, MaxLen, TempString);
248 CurrentCursor--;
249 }
250
251 default:
252 //
253 // If it is the beginning of the string, don't worry about checking maximum limits
254 //
255 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
256 StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1);
257 CurrentCursor++;
258 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
259 KeyPad[0] = Key.UnicodeChar;
260 KeyPad[1] = CHAR_NULL;
261 Count = GetStringWidth (StringPtr) / 2 - 1;
262 if (CurrentCursor < Count) {
263 for (Index = 0; Index < CurrentCursor; Index++) {
264 TempString[Index] = StringPtr[Index];
265 }
266
267 TempString[Index] = CHAR_NULL;
268 StrCatS (TempString, MaxLen, KeyPad);
269 StrCatS (TempString, MaxLen, StringPtr + CurrentCursor);
270 StrCpyS (StringPtr, MaxLen, TempString);
271 } else {
272 StrCatS (StringPtr, MaxLen, KeyPad);
273 }
274
275 CurrentCursor++;
276 }
277
278 //
279 // If the width of the input string is now larger than the screen, we nee to
280 // adjust the index to start printing portions of the string
281 //
282 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
283 PrintStringAt (Start + 1, Top + 3, BufferedString);
284
285 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
286 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
287 } else {
288 Index = 0;
289 }
290
291 if (IsPassword) {
292 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
293 }
294
295 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
296 BufferedString[Count] = StringPtr[Index];
297
298 if (IsPassword) {
299 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
300 }
301 }
302
303 if (!IsPassword) {
304 PrintStringAt (Start + 1, Top + 3, BufferedString);
305 }
306
307 break;
308 }
309
310 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
311 gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
312 } while (TRUE);
313 }
314
315 /**
316 Adjust the value to the correct one. Rules follow the sample:
317 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
318 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
319
320 @param QuestionValue Pointer to current question.
321 @param Sequence The sequence of the field in the question.
322 **/
323 VOID
324 AdjustQuestionValue (
325 IN EFI_HII_VALUE *QuestionValue,
326 IN UINT8 Sequence
327 )
328 {
329 UINT8 Month;
330 UINT16 Year;
331 UINT8 Maximum;
332 UINT8 Minimum;
333
334 Month = QuestionValue->Value.date.Month;
335 Year = QuestionValue->Value.date.Year;
336 Minimum = 1;
337
338 switch (Month) {
339 case 2:
340 if (((Year % 4) == 0) && (((Year % 100) != 0) || ((Year % 400) == 0))) {
341 Maximum = 29;
342 } else {
343 Maximum = 28;
344 }
345
346 break;
347 case 4:
348 case 6:
349 case 9:
350 case 11:
351 Maximum = 30;
352 break;
353 default:
354 Maximum = 31;
355 break;
356 }
357
358 //
359 // Change the month area.
360 //
361 if (Sequence == 0) {
362 if (QuestionValue->Value.date.Day > Maximum) {
363 QuestionValue->Value.date.Day = Maximum;
364 }
365 }
366
367 //
368 // Change the Year area.
369 //
370 if (Sequence == 2) {
371 if (QuestionValue->Value.date.Day > Maximum) {
372 QuestionValue->Value.date.Day = Minimum;
373 }
374 }
375 }
376
377 /**
378 Get field info from numeric opcode.
379
380 @param OpCode Pointer to the current input opcode.
381 @param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type.
382 @param QuestionValue Input question value, with EFI_HII_VALUE type.
383 @param Value Return question value, always return UINT64 type.
384 @param Minimum The minimum size info for this opcode.
385 @param Maximum The maximum size info for this opcode.
386 @param Step The step size info for this opcode.
387 @param StorageWidth The storage width info for this opcode.
388
389 **/
390 VOID
391 GetValueFromNum (
392 IN EFI_IFR_OP_HEADER *OpCode,
393 IN BOOLEAN IntInput,
394 IN EFI_HII_VALUE *QuestionValue,
395 OUT UINT64 *Value,
396 OUT UINT64 *Minimum,
397 OUT UINT64 *Maximum,
398 OUT UINT64 *Step,
399 OUT UINT16 *StorageWidth
400 )
401 {
402 EFI_IFR_NUMERIC *NumericOp;
403
404 NumericOp = (EFI_IFR_NUMERIC *)OpCode;
405
406 switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
407 case EFI_IFR_NUMERIC_SIZE_1:
408 if (IntInput) {
409 *Minimum = (INT64)(INT8)NumericOp->data.u8.MinValue;
410 *Maximum = (INT64)(INT8)NumericOp->data.u8.MaxValue;
411 *Value = (INT64)(INT8)QuestionValue->Value.u8;
412 } else {
413 *Minimum = NumericOp->data.u8.MinValue;
414 *Maximum = NumericOp->data.u8.MaxValue;
415 *Value = QuestionValue->Value.u8;
416 }
417
418 *Step = NumericOp->data.u8.Step;
419 *StorageWidth = (UINT16)sizeof (UINT8);
420 break;
421
422 case EFI_IFR_NUMERIC_SIZE_2:
423 if (IntInput) {
424 *Minimum = (INT64)(INT16)NumericOp->data.u16.MinValue;
425 *Maximum = (INT64)(INT16)NumericOp->data.u16.MaxValue;
426 *Value = (INT64)(INT16)QuestionValue->Value.u16;
427 } else {
428 *Minimum = NumericOp->data.u16.MinValue;
429 *Maximum = NumericOp->data.u16.MaxValue;
430 *Value = QuestionValue->Value.u16;
431 }
432
433 *Step = NumericOp->data.u16.Step;
434 *StorageWidth = (UINT16)sizeof (UINT16);
435 break;
436
437 case EFI_IFR_NUMERIC_SIZE_4:
438 if (IntInput) {
439 *Minimum = (INT64)(INT32)NumericOp->data.u32.MinValue;
440 *Maximum = (INT64)(INT32)NumericOp->data.u32.MaxValue;
441 *Value = (INT64)(INT32)QuestionValue->Value.u32;
442 } else {
443 *Minimum = NumericOp->data.u32.MinValue;
444 *Maximum = NumericOp->data.u32.MaxValue;
445 *Value = QuestionValue->Value.u32;
446 }
447
448 *Step = NumericOp->data.u32.Step;
449 *StorageWidth = (UINT16)sizeof (UINT32);
450 break;
451
452 case EFI_IFR_NUMERIC_SIZE_8:
453 if (IntInput) {
454 *Minimum = (INT64)NumericOp->data.u64.MinValue;
455 *Maximum = (INT64)NumericOp->data.u64.MaxValue;
456 *Value = (INT64)QuestionValue->Value.u64;
457 } else {
458 *Minimum = NumericOp->data.u64.MinValue;
459 *Maximum = NumericOp->data.u64.MaxValue;
460 *Value = QuestionValue->Value.u64;
461 }
462
463 *Step = NumericOp->data.u64.Step;
464 *StorageWidth = (UINT16)sizeof (UINT64);
465 break;
466
467 default:
468 break;
469 }
470
471 if (*Maximum == 0) {
472 *Maximum = (UINT64)-1;
473 }
474 }
475
476 /**
477 This routine reads a numeric value from the user input.
478
479 @param MenuOption Pointer to the current input menu.
480
481 @retval EFI_SUCCESS If numerical input is read successfully
482 @retval EFI_DEVICE_ERROR If operation fails
483
484 **/
485 EFI_STATUS
486 GetNumericInput (
487 IN UI_MENU_OPTION *MenuOption
488 )
489 {
490 UINTN Column;
491 UINTN Row;
492 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
493 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
494 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
495 UINTN Count;
496 UINTN Loop;
497 BOOLEAN ManualInput;
498 BOOLEAN HexInput;
499 BOOLEAN IntInput;
500 BOOLEAN Negative;
501 BOOLEAN ValidateFail;
502 BOOLEAN DateOrTime;
503 UINTN InputWidth;
504 UINT64 EditValue;
505 UINT64 Step;
506 UINT64 Minimum;
507 UINT64 Maximum;
508 UINTN EraseLen;
509 UINT8 Digital;
510 EFI_INPUT_KEY Key;
511 EFI_HII_VALUE *QuestionValue;
512 FORM_DISPLAY_ENGINE_STATEMENT *Question;
513 EFI_IFR_NUMERIC *NumericOp;
514 UINT16 StorageWidth;
515
516 Column = MenuOption->OptCol;
517 Row = MenuOption->Row;
518 PreviousNumber[0] = 0;
519 Count = 0;
520 InputWidth = 0;
521 Digital = 0;
522 StorageWidth = 0;
523 Minimum = 0;
524 Maximum = 0;
525 NumericOp = NULL;
526 IntInput = FALSE;
527 HexInput = FALSE;
528 Negative = FALSE;
529 ValidateFail = FALSE;
530
531 Question = MenuOption->ThisTag;
532 QuestionValue = &Question->CurrentValue;
533 ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16));
534
535 //
536 // Only two case, user can enter to this function: Enter and +/- case.
537 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
538 //
539 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
540
541 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
542 DateOrTime = TRUE;
543 } else {
544 DateOrTime = FALSE;
545 }
546
547 //
548 // Prepare Value to be edit
549 //
550 EraseLen = 0;
551 EditValue = 0;
552 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
553 Step = 1;
554 Minimum = 1;
555
556 switch (MenuOption->Sequence) {
557 case 0:
558 Maximum = 12;
559 EraseLen = 4;
560 EditValue = QuestionValue->Value.date.Month;
561 break;
562
563 case 1:
564 switch (QuestionValue->Value.date.Month) {
565 case 2:
566 if (((QuestionValue->Value.date.Year % 4) == 0) &&
567 (((QuestionValue->Value.date.Year % 100) != 0) ||
568 ((QuestionValue->Value.date.Year % 400) == 0)))
569 {
570 Maximum = 29;
571 } else {
572 Maximum = 28;
573 }
574
575 break;
576 case 4:
577 case 6:
578 case 9:
579 case 11:
580 Maximum = 30;
581 break;
582 default:
583 Maximum = 31;
584 break;
585 }
586
587 EraseLen = 3;
588 EditValue = QuestionValue->Value.date.Day;
589 break;
590
591 case 2:
592 Maximum = 0xffff;
593 EraseLen = 5;
594 EditValue = QuestionValue->Value.date.Year;
595 break;
596
597 default:
598 break;
599 }
600 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
601 Step = 1;
602 Minimum = 0;
603
604 switch (MenuOption->Sequence) {
605 case 0:
606 Maximum = 23;
607 EraseLen = 4;
608 EditValue = QuestionValue->Value.time.Hour;
609 break;
610
611 case 1:
612 Maximum = 59;
613 EraseLen = 3;
614 EditValue = QuestionValue->Value.time.Minute;
615 break;
616
617 case 2:
618 Maximum = 59;
619 EraseLen = 3;
620 EditValue = QuestionValue->Value.time.Second;
621 break;
622
623 default:
624 break;
625 }
626 } else {
627 ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
628 NumericOp = (EFI_IFR_NUMERIC *)Question->OpCode;
629 GetValueFromNum (Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);
630 EraseLen = gOptionBlockWidth;
631 }
632
633 if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {
634 if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX) {
635 HexInput = TRUE;
636 } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0) {
637 //
638 // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.
639 //
640 IntInput = TRUE;
641 }
642 }
643
644 //
645 // Enter from "Enter" input, clear the old word showing.
646 //
647 if (ManualInput) {
648 if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
649 if (HexInput) {
650 InputWidth = StorageWidth * 2;
651 } else {
652 switch (StorageWidth) {
653 case 1:
654 InputWidth = 3;
655 break;
656
657 case 2:
658 InputWidth = 5;
659 break;
660
661 case 4:
662 InputWidth = 10;
663 break;
664
665 case 8:
666 InputWidth = 20;
667 break;
668
669 default:
670 InputWidth = 0;
671 break;
672 }
673
674 if (IntInput) {
675 //
676 // Support an extra '-' for negative number.
677 //
678 InputWidth += 1;
679 }
680 }
681
682 InputText[0] = LEFT_NUMERIC_DELIMITER;
683 SetUnicodeMem (InputText + 1, InputWidth, L' ');
684 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
685 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
686 InputText[InputWidth + 2] = L'\0';
687
688 PrintStringAt (Column, Row, InputText);
689 Column++;
690 }
691
692 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
693 if (MenuOption->Sequence == 2) {
694 InputWidth = 4;
695 } else {
696 InputWidth = 2;
697 }
698
699 if (MenuOption->Sequence == 0) {
700 InputText[0] = LEFT_NUMERIC_DELIMITER;
701 SetUnicodeMem (InputText + 1, InputWidth, L' ');
702 InputText[InputWidth + 1] = DATE_SEPARATOR;
703 InputText[InputWidth + 2] = L'\0';
704 } else if (MenuOption->Sequence == 1) {
705 SetUnicodeMem (InputText, InputWidth, L' ');
706 InputText[InputWidth] = DATE_SEPARATOR;
707 InputText[InputWidth + 1] = L'\0';
708 } else {
709 SetUnicodeMem (InputText, InputWidth, L' ');
710 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
711 InputText[InputWidth + 1] = L'\0';
712 }
713
714 PrintStringAt (Column, Row, InputText);
715 if (MenuOption->Sequence == 0) {
716 Column++;
717 }
718 }
719
720 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
721 InputWidth = 2;
722
723 if (MenuOption->Sequence == 0) {
724 InputText[0] = LEFT_NUMERIC_DELIMITER;
725 SetUnicodeMem (InputText + 1, InputWidth, L' ');
726 InputText[InputWidth + 1] = TIME_SEPARATOR;
727 InputText[InputWidth + 2] = L'\0';
728 } else if (MenuOption->Sequence == 1) {
729 SetUnicodeMem (InputText, InputWidth, L' ');
730 InputText[InputWidth] = TIME_SEPARATOR;
731 InputText[InputWidth + 1] = L'\0';
732 } else {
733 SetUnicodeMem (InputText, InputWidth, L' ');
734 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
735 InputText[InputWidth + 1] = L'\0';
736 }
737
738 PrintStringAt (Column, Row, InputText);
739 if (MenuOption->Sequence == 0) {
740 Column++;
741 }
742 }
743 }
744
745 //
746 // First time we enter this handler, we need to check to see if
747 // we were passed an increment or decrement directive
748 //
749 do {
750 Key.UnicodeChar = CHAR_NULL;
751 if (gDirection != 0) {
752 Key.ScanCode = gDirection;
753 gDirection = 0;
754 goto TheKey2;
755 }
756
757 WaitForKeyStroke (&Key);
758
759 TheKey2:
760 switch (Key.UnicodeChar) {
761 case '+':
762 case '-':
763 if (ManualInput && IntInput) {
764 //
765 // In Manual input mode, check whether input the negative flag.
766 //
767 if (Key.UnicodeChar == '-') {
768 if (Negative) {
769 break;
770 }
771
772 Negative = TRUE;
773 PrintCharAt (Column++, Row, Key.UnicodeChar);
774 }
775 } else {
776 if (Key.UnicodeChar == '+') {
777 Key.ScanCode = SCAN_RIGHT;
778 } else {
779 Key.ScanCode = SCAN_LEFT;
780 }
781
782 Key.UnicodeChar = CHAR_NULL;
783 goto TheKey2;
784 }
785
786 break;
787
788 case CHAR_NULL:
789 switch (Key.ScanCode) {
790 case SCAN_LEFT:
791 case SCAN_RIGHT:
792 if (DateOrTime && !ManualInput) {
793 //
794 // By setting this value, we will return back to the caller.
795 // We need to do this since an auto-refresh will destroy the adjustment
796 // based on what the real-time-clock is showing. So we always commit
797 // upon changing the value.
798 //
799 gDirection = SCAN_DOWN;
800 }
801
802 if ((Step != 0) && !ManualInput) {
803 if (Key.ScanCode == SCAN_LEFT) {
804 if (IntInput) {
805 if ((INT64)EditValue >= (INT64)Minimum + (INT64)Step) {
806 EditValue = EditValue - Step;
807 } else if ((INT64)EditValue > (INT64)Minimum) {
808 EditValue = Minimum;
809 } else {
810 EditValue = Maximum;
811 }
812 } else {
813 if (EditValue >= Minimum + Step) {
814 EditValue = EditValue - Step;
815 } else if (EditValue > Minimum) {
816 EditValue = Minimum;
817 } else {
818 EditValue = Maximum;
819 }
820 }
821 } else if (Key.ScanCode == SCAN_RIGHT) {
822 if (IntInput) {
823 if ((INT64)EditValue + (INT64)Step <= (INT64)Maximum) {
824 EditValue = EditValue + Step;
825 } else if ((INT64)EditValue < (INT64)Maximum) {
826 EditValue = Maximum;
827 } else {
828 EditValue = Minimum;
829 }
830 } else {
831 if (EditValue + Step <= Maximum) {
832 EditValue = EditValue + Step;
833 } else if (EditValue < Maximum) {
834 EditValue = Maximum;
835 } else {
836 EditValue = Minimum;
837 }
838 }
839 }
840
841 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
842 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
843 if (MenuOption->Sequence == 2) {
844 //
845 // Year
846 //
847 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16)EditValue);
848 } else {
849 //
850 // Month/Day
851 //
852 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8)EditValue);
853 }
854
855 if (MenuOption->Sequence == 0) {
856 ASSERT (EraseLen >= 2);
857 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
858 } else if (MenuOption->Sequence == 1) {
859 ASSERT (EraseLen >= 1);
860 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
861 }
862 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
863 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8)EditValue);
864
865 if (MenuOption->Sequence == 0) {
866 ASSERT (EraseLen >= 2);
867 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
868 } else if (MenuOption->Sequence == 1) {
869 ASSERT (EraseLen >= 1);
870 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
871 }
872 } else {
873 QuestionValue->Value.u64 = EditValue;
874 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
875 }
876
877 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
878 for (Loop = 0; Loop < EraseLen; Loop++) {
879 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
880 }
881
882 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
883
884 if (MenuOption->Sequence == 0) {
885 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
886 Column = MenuOption->OptCol + 1;
887 }
888
889 PrintStringAt (Column, Row, FormattedNumber);
890
891 if (!DateOrTime || (MenuOption->Sequence == 2)) {
892 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
893 }
894 }
895
896 goto EnterCarriageReturn;
897
898 case SCAN_UP:
899 case SCAN_DOWN:
900 goto EnterCarriageReturn;
901
902 case SCAN_ESC:
903 return EFI_DEVICE_ERROR;
904
905 default:
906 break;
907 }
908
909 break;
910
911 EnterCarriageReturn:
912
913 case CHAR_CARRIAGE_RETURN:
914 //
915 // Validate input value with Minimum value.
916 //
917 ValidateFail = FALSE;
918 if (IntInput) {
919 //
920 // After user input Enter, need to check whether the input value.
921 // If input a negative value, should compare with maximum value.
922 // else compare with the minimum value.
923 //
924 if (Negative) {
925 ValidateFail = (INT64)EditValue > (INT64)Maximum ? TRUE : FALSE;
926 } else {
927 ValidateFail = (INT64)EditValue < (INT64)Minimum ? TRUE : FALSE;
928 }
929
930 if (ValidateFail) {
931 UpdateStatusBar (INPUT_ERROR, TRUE);
932 break;
933 }
934 } else if (EditValue < Minimum) {
935 UpdateStatusBar (INPUT_ERROR, TRUE);
936 break;
937 }
938
939 UpdateStatusBar (INPUT_ERROR, FALSE);
940 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
941 QuestionValue = &gUserInput->InputValue;
942 //
943 // Store Edit value back to Question
944 //
945 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
946 switch (MenuOption->Sequence) {
947 case 0:
948 QuestionValue->Value.date.Month = (UINT8)EditValue;
949 break;
950
951 case 1:
952 QuestionValue->Value.date.Day = (UINT8)EditValue;
953 break;
954
955 case 2:
956 QuestionValue->Value.date.Year = (UINT16)EditValue;
957 break;
958
959 default:
960 break;
961 }
962 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
963 switch (MenuOption->Sequence) {
964 case 0:
965 QuestionValue->Value.time.Hour = (UINT8)EditValue;
966 break;
967
968 case 1:
969 QuestionValue->Value.time.Minute = (UINT8)EditValue;
970 break;
971
972 case 2:
973 QuestionValue->Value.time.Second = (UINT8)EditValue;
974 break;
975
976 default:
977 break;
978 }
979 } else {
980 //
981 // Numeric
982 //
983 QuestionValue->Value.u64 = EditValue;
984 }
985
986 //
987 // Adjust the value to the correct one.
988 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
989 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
990 //
991 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) &&
992 ((MenuOption->Sequence == 0) || (MenuOption->Sequence == 2)))
993 {
994 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
995 }
996
997 return EFI_SUCCESS;
998
999 case CHAR_BACKSPACE:
1000 if (ManualInput) {
1001 if (Count == 0) {
1002 if (Negative) {
1003 Negative = FALSE;
1004 Column--;
1005 PrintStringAt (Column, Row, L" ");
1006 }
1007
1008 break;
1009 }
1010
1011 //
1012 // Remove a character
1013 //
1014 EditValue = PreviousNumber[Count - 1];
1015 UpdateStatusBar (INPUT_ERROR, FALSE);
1016 Count--;
1017 Column--;
1018 PrintStringAt (Column, Row, L" ");
1019 }
1020
1021 break;
1022
1023 default:
1024 if (ManualInput) {
1025 if (HexInput) {
1026 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
1027 Digital = (UINT8)(Key.UnicodeChar - L'0');
1028 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
1029 Digital = (UINT8)(Key.UnicodeChar - L'A' + 0x0A);
1030 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
1031 Digital = (UINT8)(Key.UnicodeChar - L'a' + 0x0A);
1032 } else {
1033 UpdateStatusBar (INPUT_ERROR, TRUE);
1034 break;
1035 }
1036 } else {
1037 if ((Key.UnicodeChar > L'9') || (Key.UnicodeChar < L'0')) {
1038 UpdateStatusBar (INPUT_ERROR, TRUE);
1039 break;
1040 }
1041 }
1042
1043 //
1044 // If Count exceed input width, there is no way more is valid
1045 //
1046 if (Count >= InputWidth) {
1047 break;
1048 }
1049
1050 //
1051 // Someone typed something valid!
1052 //
1053 if (Count != 0) {
1054 if (HexInput) {
1055 EditValue = LShiftU64 (EditValue, 4) + Digital;
1056 } else if (IntInput && Negative) {
1057 //
1058 // Save the negative number.
1059 //
1060 EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;
1061 } else {
1062 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
1063 }
1064 } else {
1065 if (HexInput) {
1066 EditValue = Digital;
1067 } else if (IntInput && Negative) {
1068 //
1069 // Save the negative number.
1070 //
1071 EditValue = ~(Key.UnicodeChar - L'0') + 1;
1072 } else {
1073 EditValue = Key.UnicodeChar - L'0';
1074 }
1075 }
1076
1077 if (IntInput) {
1078 ValidateFail = FALSE;
1079 //
1080 // When user input a new value, should check the current value.
1081 // If user input a negative value, should compare it with minimum
1082 // value, else compare it with maximum value.
1083 //
1084 if (Negative) {
1085 ValidateFail = (INT64)EditValue < (INT64)Minimum ? TRUE : FALSE;
1086 } else {
1087 ValidateFail = (INT64)EditValue > (INT64)Maximum ? TRUE : FALSE;
1088 }
1089
1090 if (ValidateFail) {
1091 UpdateStatusBar (INPUT_ERROR, TRUE);
1092 ASSERT (Count < ARRAY_SIZE (PreviousNumber));
1093 EditValue = PreviousNumber[Count];
1094 break;
1095 }
1096 } else {
1097 if (EditValue > Maximum) {
1098 UpdateStatusBar (INPUT_ERROR, TRUE);
1099 ASSERT (Count < ARRAY_SIZE (PreviousNumber));
1100 EditValue = PreviousNumber[Count];
1101 break;
1102 }
1103 }
1104
1105 UpdateStatusBar (INPUT_ERROR, FALSE);
1106
1107 Count++;
1108 ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));
1109 PreviousNumber[Count] = EditValue;
1110
1111 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
1112 PrintCharAt (Column, Row, Key.UnicodeChar);
1113 Column++;
1114 }
1115
1116 break;
1117 }
1118 } while (TRUE);
1119 }
1120
1121 /**
1122 Adjust option order base on the question value.
1123
1124 @param Question Pointer to current question.
1125 @param PopUpMenuLines The line number of the pop up menu.
1126
1127 @retval EFI_SUCCESS If Option input is processed successfully
1128 @retval EFI_DEVICE_ERROR If operation fails
1129
1130 **/
1131 EFI_STATUS
1132 AdjustOptionOrder (
1133 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
1134 OUT UINTN *PopUpMenuLines
1135 )
1136 {
1137 UINTN Index;
1138 EFI_IFR_ORDERED_LIST *OrderList;
1139 UINT8 *ValueArray;
1140 UINT8 ValueType;
1141 LIST_ENTRY *Link;
1142 DISPLAY_QUESTION_OPTION *OneOfOption;
1143 EFI_HII_VALUE *HiiValueArray;
1144
1145 Link = GetFirstNode (&Question->OptionListHead);
1146 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1147 ValueArray = Question->CurrentValue.Buffer;
1148 ValueType = OneOfOption->OptionOpCode->Type;
1149 OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
1150
1151 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1152 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
1153 break;
1154 }
1155 }
1156
1157 *PopUpMenuLines = Index;
1158
1159 //
1160 // Prepare HiiValue array
1161 //
1162 HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
1163 ASSERT (HiiValueArray != NULL);
1164
1165 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1166 HiiValueArray[Index].Type = ValueType;
1167 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1168 }
1169
1170 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1171 OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
1172 if (OneOfOption == NULL) {
1173 return EFI_NOT_FOUND;
1174 }
1175
1176 RemoveEntryList (&OneOfOption->Link);
1177
1178 //
1179 // Insert to head.
1180 //
1181 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
1182 }
1183
1184 FreePool (HiiValueArray);
1185
1186 return EFI_SUCCESS;
1187 }
1188
1189 /**
1190 Base on the type to compare the value.
1191
1192 @param Value1 The first value need to compare.
1193 @param Value2 The second value need to compare.
1194 @param Type The value type for above two values.
1195
1196 @retval TRUE The two value are same.
1197 @retval FALSE The two value are different.
1198
1199 **/
1200 BOOLEAN
1201 IsValuesEqual (
1202 IN EFI_IFR_TYPE_VALUE *Value1,
1203 IN EFI_IFR_TYPE_VALUE *Value2,
1204 IN UINT8 Type
1205 )
1206 {
1207 switch (Type) {
1208 case EFI_IFR_TYPE_BOOLEAN:
1209 case EFI_IFR_TYPE_NUM_SIZE_8:
1210 return (BOOLEAN)(Value1->u8 == Value2->u8);
1211
1212 case EFI_IFR_TYPE_NUM_SIZE_16:
1213 return (BOOLEAN)(Value1->u16 == Value2->u16);
1214
1215 case EFI_IFR_TYPE_NUM_SIZE_32:
1216 return (BOOLEAN)(Value1->u32 == Value2->u32);
1217
1218 case EFI_IFR_TYPE_NUM_SIZE_64:
1219 return (BOOLEAN)(Value1->u64 == Value2->u64);
1220
1221 default:
1222 ASSERT (FALSE);
1223 return FALSE;
1224 }
1225 }
1226
1227 /**
1228 Base on the type to set the value.
1229
1230 @param Dest The dest value.
1231 @param Source The source value.
1232 @param Type The value type for above two values.
1233
1234 **/
1235 VOID
1236 SetValuesByType (
1237 OUT EFI_IFR_TYPE_VALUE *Dest,
1238 IN EFI_IFR_TYPE_VALUE *Source,
1239 IN UINT8 Type
1240 )
1241 {
1242 switch (Type) {
1243 case EFI_IFR_TYPE_BOOLEAN:
1244 Dest->b = Source->b;
1245 break;
1246
1247 case EFI_IFR_TYPE_NUM_SIZE_8:
1248 Dest->u8 = Source->u8;
1249 break;
1250
1251 case EFI_IFR_TYPE_NUM_SIZE_16:
1252 Dest->u16 = Source->u16;
1253 break;
1254
1255 case EFI_IFR_TYPE_NUM_SIZE_32:
1256 Dest->u32 = Source->u32;
1257 break;
1258
1259 case EFI_IFR_TYPE_NUM_SIZE_64:
1260 Dest->u64 = Source->u64;
1261 break;
1262
1263 default:
1264 ASSERT (FALSE);
1265 break;
1266 }
1267 }
1268
1269 /**
1270 Get selection for OneOf and OrderedList (Left/Right will be ignored).
1271
1272 @param MenuOption Pointer to the current input menu.
1273
1274 @retval EFI_SUCCESS If Option input is processed successfully
1275 @retval EFI_DEVICE_ERROR If operation fails
1276
1277 **/
1278 EFI_STATUS
1279 GetSelectionInputPopUp (
1280 IN UI_MENU_OPTION *MenuOption
1281 )
1282 {
1283 EFI_INPUT_KEY Key;
1284 UINTN Index;
1285 CHAR16 *StringPtr;
1286 CHAR16 *TempStringPtr;
1287 UINTN Index2;
1288 UINTN TopOptionIndex;
1289 UINTN HighlightOptionIndex;
1290 UINTN Start;
1291 UINTN End;
1292 UINTN Top;
1293 UINTN Bottom;
1294 UINTN PopUpMenuLines;
1295 UINTN MenuLinesInView;
1296 UINTN PopUpWidth;
1297 CHAR16 Character;
1298 INT32 SavedAttribute;
1299 BOOLEAN ShowDownArrow;
1300 BOOLEAN ShowUpArrow;
1301 UINTN DimensionsWidth;
1302 LIST_ENTRY *Link;
1303 BOOLEAN OrderedList;
1304 UINT8 *ValueArray;
1305 UINT8 *ReturnValue;
1306 UINT8 ValueType;
1307 EFI_HII_VALUE HiiValue;
1308 DISPLAY_QUESTION_OPTION *OneOfOption;
1309 DISPLAY_QUESTION_OPTION *CurrentOption;
1310 FORM_DISPLAY_ENGINE_STATEMENT *Question;
1311 INTN Result;
1312 EFI_IFR_ORDERED_LIST *OrderList;
1313
1314 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
1315
1316 ValueArray = NULL;
1317 ValueType = 0;
1318 CurrentOption = NULL;
1319 ShowDownArrow = FALSE;
1320 ShowUpArrow = FALSE;
1321
1322 ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
1323
1324 Question = MenuOption->ThisTag;
1325 if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
1326 Link = GetFirstNode (&Question->OptionListHead);
1327 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1328 ValueArray = Question->CurrentValue.Buffer;
1329 ValueType = OneOfOption->OptionOpCode->Type;
1330 OrderedList = TRUE;
1331 OrderList = (EFI_IFR_ORDERED_LIST *)Question->OpCode;
1332 } else {
1333 OrderedList = FALSE;
1334 OrderList = NULL;
1335 }
1336
1337 //
1338 // Calculate Option count
1339 //
1340 PopUpMenuLines = 0;
1341 if (OrderedList) {
1342 AdjustOptionOrder (Question, &PopUpMenuLines);
1343 } else {
1344 Link = GetFirstNode (&Question->OptionListHead);
1345 while (!IsNull (&Question->OptionListHead, Link)) {
1346 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1347 PopUpMenuLines++;
1348 Link = GetNextNode (&Question->OptionListHead, Link);
1349 }
1350 }
1351
1352 //
1353 // Get the number of one of options present and its size
1354 //
1355 PopUpWidth = 0;
1356 HighlightOptionIndex = 0;
1357 Link = GetFirstNode (&Question->OptionListHead);
1358 for (Index = 0; Index < PopUpMenuLines; Index++) {
1359 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1360
1361 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1362 if (StrLen (StringPtr) > PopUpWidth) {
1363 PopUpWidth = StrLen (StringPtr);
1364 }
1365
1366 FreePool (StringPtr);
1367 HiiValue.Type = OneOfOption->OptionOpCode->Type;
1368 SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
1369 if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
1370 //
1371 // Find current selected Option for OneOf
1372 //
1373 HighlightOptionIndex = Index;
1374 }
1375
1376 Link = GetNextNode (&Question->OptionListHead, Link);
1377 }
1378
1379 //
1380 // Perform popup menu initialization.
1381 //
1382 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1383
1384 SavedAttribute = gST->ConOut->Mode->Attribute;
1385 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1386
1387 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1388 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1389 }
1390
1391 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
1392 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1393 Top = gStatementDimensions.TopRow;
1394 Bottom = gStatementDimensions.BottomRow - 1;
1395
1396 MenuLinesInView = Bottom - Top - 1;
1397 if (MenuLinesInView >= PopUpMenuLines) {
1398 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1399 Bottom = Top + PopUpMenuLines + 1;
1400 } else {
1401 ShowDownArrow = TRUE;
1402 }
1403
1404 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1405 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1406 } else {
1407 TopOptionIndex = 0;
1408 }
1409
1410 do {
1411 //
1412 // Clear that portion of the screen
1413 //
1414 ClearLines (Start, End, Top, Bottom, GetPopupColor ());
1415
1416 //
1417 // Draw "One of" pop-up menu
1418 //
1419 Character = BOXDRAW_DOWN_RIGHT;
1420 PrintCharAt (Start, Top, Character);
1421 for (Index = Start; Index + 2 < End; Index++) {
1422 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1423 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1424 } else {
1425 Character = BOXDRAW_HORIZONTAL;
1426 }
1427
1428 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1429 }
1430
1431 Character = BOXDRAW_DOWN_LEFT;
1432 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1433 Character = BOXDRAW_VERTICAL;
1434 for (Index = Top + 1; Index < Bottom; Index++) {
1435 PrintCharAt (Start, Index, Character);
1436 PrintCharAt (End - 1, Index, Character);
1437 }
1438
1439 //
1440 // Move to top Option
1441 //
1442 Link = GetFirstNode (&Question->OptionListHead);
1443 for (Index = 0; Index < TopOptionIndex; Index++) {
1444 Link = GetNextNode (&Question->OptionListHead, Link);
1445 }
1446
1447 //
1448 // Display the One of options
1449 //
1450 Index2 = Top + 1;
1451 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1452 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1453 Link = GetNextNode (&Question->OptionListHead, Link);
1454
1455 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1456 ASSERT (StringPtr != NULL);
1457 //
1458 // If the string occupies multiple lines, truncate it to fit in one line,
1459 // and append a "..." for indication.
1460 //
1461 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1462 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1463 ASSERT (TempStringPtr != NULL);
1464 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1465 FreePool (StringPtr);
1466 StringPtr = TempStringPtr;
1467 StrCatS (StringPtr, PopUpWidth - 1, L"...");
1468 }
1469
1470 if (Index == HighlightOptionIndex) {
1471 //
1472 // Highlight the selected one
1473 //
1474 CurrentOption = OneOfOption;
1475
1476 gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
1477 PrintStringAt (Start + 2, Index2, StringPtr);
1478 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1479 } else {
1480 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1481 PrintStringAt (Start + 2, Index2, StringPtr);
1482 }
1483
1484 Index2++;
1485 FreePool (StringPtr);
1486 }
1487
1488 Character = BOXDRAW_UP_RIGHT;
1489 PrintCharAt (Start, Bottom, Character);
1490 for (Index = Start; Index + 2 < End; Index++) {
1491 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1492 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1493 } else {
1494 Character = BOXDRAW_HORIZONTAL;
1495 }
1496
1497 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1498 }
1499
1500 Character = BOXDRAW_UP_LEFT;
1501 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1502
1503 //
1504 // Get User selection
1505 //
1506 Key.UnicodeChar = CHAR_NULL;
1507 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1508 Key.ScanCode = gDirection;
1509 gDirection = 0;
1510 goto TheKey;
1511 }
1512
1513 WaitForKeyStroke (&Key);
1514
1515 TheKey:
1516 switch (Key.UnicodeChar) {
1517 case '+':
1518 if (OrderedList) {
1519 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1520 //
1521 // Highlight reaches the top of the popup window, scroll one menu item.
1522 //
1523 TopOptionIndex--;
1524 ShowDownArrow = TRUE;
1525 }
1526
1527 if (TopOptionIndex == 0) {
1528 ShowUpArrow = FALSE;
1529 }
1530
1531 if (HighlightOptionIndex > 0) {
1532 HighlightOptionIndex--;
1533
1534 ASSERT (CurrentOption != NULL);
1535 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1536 }
1537 }
1538
1539 break;
1540
1541 case '-':
1542 //
1543 // If an ordered list op-code, we will allow for a popup of +/- keys
1544 // to create an ordered list of items
1545 //
1546 if (OrderedList) {
1547 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1548 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1)))
1549 {
1550 //
1551 // Highlight reaches the bottom of the popup window, scroll one menu item.
1552 //
1553 TopOptionIndex++;
1554 ShowUpArrow = TRUE;
1555 }
1556
1557 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1558 ShowDownArrow = FALSE;
1559 }
1560
1561 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1562 HighlightOptionIndex++;
1563
1564 ASSERT (CurrentOption != NULL);
1565 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1566 }
1567 }
1568
1569 break;
1570
1571 case CHAR_NULL:
1572 switch (Key.ScanCode) {
1573 case SCAN_UP:
1574 case SCAN_DOWN:
1575 if (Key.ScanCode == SCAN_UP) {
1576 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1577 //
1578 // Highlight reaches the top of the popup window, scroll one menu item.
1579 //
1580 TopOptionIndex--;
1581 ShowDownArrow = TRUE;
1582 }
1583
1584 if (TopOptionIndex == 0) {
1585 ShowUpArrow = FALSE;
1586 }
1587
1588 if (HighlightOptionIndex > 0) {
1589 HighlightOptionIndex--;
1590 }
1591 } else {
1592 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1593 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1)))
1594 {
1595 //
1596 // Highlight reaches the bottom of the popup window, scroll one menu item.
1597 //
1598 TopOptionIndex++;
1599 ShowUpArrow = TRUE;
1600 }
1601
1602 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1603 ShowDownArrow = FALSE;
1604 }
1605
1606 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1607 HighlightOptionIndex++;
1608 }
1609 }
1610
1611 break;
1612
1613 case SCAN_ESC:
1614 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1615
1616 //
1617 // Restore link list order for orderedlist
1618 //
1619 if (OrderedList) {
1620 HiiValue.Type = ValueType;
1621 HiiValue.Value.u64 = 0;
1622 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1623 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1624 if (HiiValue.Value.u64 == 0) {
1625 break;
1626 }
1627
1628 OneOfOption = ValueToOption (Question, &HiiValue);
1629 if (OneOfOption == NULL) {
1630 return EFI_NOT_FOUND;
1631 }
1632
1633 RemoveEntryList (&OneOfOption->Link);
1634 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1635 }
1636 }
1637
1638 return EFI_DEVICE_ERROR;
1639
1640 default:
1641 break;
1642 }
1643
1644 break;
1645
1646 case CHAR_CARRIAGE_RETURN:
1647 //
1648 // return the current selection
1649 //
1650 if (OrderedList) {
1651 ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
1652 ASSERT (ReturnValue != NULL);
1653 Index = 0;
1654 Link = GetFirstNode (&Question->OptionListHead);
1655 while (!IsNull (&Question->OptionListHead, Link)) {
1656 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1657 Link = GetNextNode (&Question->OptionListHead, Link);
1658
1659 SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
1660
1661 Index++;
1662 if (Index > OrderList->MaxContainers) {
1663 break;
1664 }
1665 }
1666
1667 if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
1668 FreePool (ReturnValue);
1669 return EFI_DEVICE_ERROR;
1670 } else {
1671 gUserInput->InputValue.Buffer = ReturnValue;
1672 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1673 }
1674 } else {
1675 ASSERT (CurrentOption != NULL);
1676 gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
1677 if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
1678 return EFI_DEVICE_ERROR;
1679 } else {
1680 SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
1681 }
1682 }
1683
1684 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1685
1686 return EFI_SUCCESS;
1687
1688 default:
1689 break;
1690 }
1691 } while (TRUE);
1692 }