VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/FirmwareNew/MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c@ 108794

Last change on this file since 108794 was 108794, checked in by vboxsync, 2 weeks ago

Devices/EFI/FirmwareNew: Merge edk2-stable202502 from the vendor branch and make it build for the important platforms, bugref:4643

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette