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