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
GetFieldFromOp(IN EFI_IFR_OP_HEADER * OpCode,OUT UINTN * Minimum,OUT UINTN * Maximum)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
ReadString(IN UI_MENU_OPTION * MenuOption,IN CHAR16 * Prompt,IN OUT CHAR16 * StringPtr)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
AdjustQuestionValue(IN EFI_HII_VALUE * QuestionValue,IN UINT8 Sequence)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
GetValueFromNum(IN EFI_IFR_OP_HEADER * OpCode,IN BOOLEAN IntInput,IN EFI_HII_VALUE * QuestionValue,OUT UINT64 * Value,OUT UINT64 * Minimum,OUT UINT64 * Maximum,OUT UINT64 * Step,OUT UINT16 * StorageWidth)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
GetNumericInput(IN UI_MENU_OPTION * MenuOption)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
AdjustOptionOrder(IN FORM_DISPLAY_ENGINE_STATEMENT * Question,OUT UINTN * PopUpMenuLines)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
IsValuesEqual(IN EFI_IFR_TYPE_VALUE * Value1,IN EFI_IFR_TYPE_VALUE * Value2,IN UINT8 Type)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
SetValuesByType(OUT EFI_IFR_TYPE_VALUE * Dest,IN EFI_IFR_TYPE_VALUE * Source,IN UINT8 Type)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
GetSelectionInputPopUp(IN UI_MENU_OPTION * MenuOption)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 
1665