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