• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /** @file
2 Implementation for handling user input from the User Interfaces.
3 
4 Copyright (c) 2004 - 2015, 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 < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
1074             EditValue = PreviousNumber[Count];
1075             break;
1076           }
1077         } else {
1078           if (EditValue > Maximum) {
1079             UpdateStatusBar (INPUT_ERROR, TRUE);
1080             ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
1081             EditValue = PreviousNumber[Count];
1082             break;
1083           }
1084         }
1085 
1086         UpdateStatusBar (INPUT_ERROR, FALSE);
1087 
1088         Count++;
1089         ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
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   StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
1303   ASSERT (StringPtr);
1304 
1305   ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
1306 
1307   Question = MenuOption->ThisTag;
1308   if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
1309     Link = GetFirstNode (&Question->OptionListHead);
1310     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1311     ValueArray = Question->CurrentValue.Buffer;
1312     ValueType =  OneOfOption->OptionOpCode->Type;
1313     OrderedList = TRUE;
1314     OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
1315   } else {
1316     OrderedList = FALSE;
1317     OrderList = NULL;
1318   }
1319 
1320   //
1321   // Calculate Option count
1322   //
1323   PopUpMenuLines = 0;
1324   if (OrderedList) {
1325     AdjustOptionOrder(Question, &PopUpMenuLines);
1326   } else {
1327     Link = GetFirstNode (&Question->OptionListHead);
1328     while (!IsNull (&Question->OptionListHead, Link)) {
1329       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1330       PopUpMenuLines++;
1331       Link = GetNextNode (&Question->OptionListHead, Link);
1332     }
1333   }
1334 
1335   //
1336   // Get the number of one of options present and its size
1337   //
1338   PopUpWidth = 0;
1339   HighlightOptionIndex = 0;
1340   Link = GetFirstNode (&Question->OptionListHead);
1341   for (Index = 0; Index < PopUpMenuLines; Index++) {
1342     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1343 
1344     StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1345     if (StrLen (StringPtr) > PopUpWidth) {
1346       PopUpWidth = StrLen (StringPtr);
1347     }
1348     FreePool (StringPtr);
1349     HiiValue.Type = OneOfOption->OptionOpCode->Type;
1350     SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
1351     if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
1352       //
1353       // Find current selected Option for OneOf
1354       //
1355       HighlightOptionIndex = Index;
1356     }
1357 
1358     Link = GetNextNode (&Question->OptionListHead, Link);
1359   }
1360 
1361   //
1362   // Perform popup menu initialization.
1363   //
1364   PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1365 
1366   SavedAttribute = gST->ConOut->Mode->Attribute;
1367   gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1368 
1369   if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1370     PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1371   }
1372 
1373   Start  = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
1374   End    = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1375   Top    = gStatementDimensions.TopRow;
1376   Bottom = gStatementDimensions.BottomRow - 1;
1377 
1378   MenuLinesInView = Bottom - Top - 1;
1379   if (MenuLinesInView >= PopUpMenuLines) {
1380     Top     = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1381     Bottom  = Top + PopUpMenuLines + 1;
1382   } else {
1383     ShowDownArrow = TRUE;
1384   }
1385 
1386   if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1387     TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1388   } else {
1389     TopOptionIndex = 0;
1390   }
1391 
1392   do {
1393     //
1394     // Clear that portion of the screen
1395     //
1396     ClearLines (Start, End, Top, Bottom, GetPopupColor ());
1397 
1398     //
1399     // Draw "One of" pop-up menu
1400     //
1401     Character = BOXDRAW_DOWN_RIGHT;
1402     PrintCharAt (Start, Top, Character);
1403     for (Index = Start; Index + 2 < End; Index++) {
1404       if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1405         Character = GEOMETRICSHAPE_UP_TRIANGLE;
1406       } else {
1407         Character = BOXDRAW_HORIZONTAL;
1408       }
1409 
1410       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1411     }
1412 
1413     Character = BOXDRAW_DOWN_LEFT;
1414     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1415     Character = BOXDRAW_VERTICAL;
1416     for (Index = Top + 1; Index < Bottom; Index++) {
1417       PrintCharAt (Start, Index, Character);
1418       PrintCharAt (End - 1, Index, Character);
1419     }
1420 
1421     //
1422     // Move to top Option
1423     //
1424     Link = GetFirstNode (&Question->OptionListHead);
1425     for (Index = 0; Index < TopOptionIndex; Index++) {
1426       Link = GetNextNode (&Question->OptionListHead, Link);
1427     }
1428 
1429     //
1430     // Display the One of options
1431     //
1432     Index2 = Top + 1;
1433     for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1434       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1435       Link = GetNextNode (&Question->OptionListHead, Link);
1436 
1437       StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1438       ASSERT (StringPtr != NULL);
1439       //
1440       // If the string occupies multiple lines, truncate it to fit in one line,
1441       // and append a "..." for indication.
1442       //
1443       if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1444         TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1445         ASSERT ( TempStringPtr != NULL );
1446         CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1447         FreePool (StringPtr);
1448         StringPtr = TempStringPtr;
1449         StrCatS (StringPtr, PopUpWidth - 1, L"...");
1450       }
1451 
1452       if (Index == HighlightOptionIndex) {
1453           //
1454           // Highlight the selected one
1455           //
1456           CurrentOption = OneOfOption;
1457 
1458           gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
1459           PrintStringAt (Start + 2, Index2, StringPtr);
1460           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1461         } else {
1462           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1463           PrintStringAt (Start + 2, Index2, StringPtr);
1464         }
1465 
1466       Index2++;
1467       FreePool (StringPtr);
1468     }
1469 
1470     Character = BOXDRAW_UP_RIGHT;
1471     PrintCharAt (Start, Bottom, Character);
1472     for (Index = Start; Index + 2 < End; Index++) {
1473       if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1474         Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1475       } else {
1476         Character = BOXDRAW_HORIZONTAL;
1477       }
1478 
1479       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1480     }
1481 
1482     Character = BOXDRAW_UP_LEFT;
1483     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1484 
1485     //
1486     // Get User selection
1487     //
1488     Key.UnicodeChar = CHAR_NULL;
1489     if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1490       Key.ScanCode  = gDirection;
1491       gDirection    = 0;
1492       goto TheKey;
1493     }
1494 
1495     WaitForKeyStroke (&Key);
1496 
1497 TheKey:
1498     switch (Key.UnicodeChar) {
1499     case '+':
1500       if (OrderedList) {
1501         if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1502           //
1503           // Highlight reaches the top of the popup window, scroll one menu item.
1504           //
1505           TopOptionIndex--;
1506           ShowDownArrow = TRUE;
1507         }
1508 
1509         if (TopOptionIndex == 0) {
1510           ShowUpArrow = FALSE;
1511         }
1512 
1513         if (HighlightOptionIndex > 0) {
1514           HighlightOptionIndex--;
1515 
1516           ASSERT (CurrentOption != NULL);
1517           SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1518         }
1519       }
1520       break;
1521 
1522     case '-':
1523       //
1524       // If an ordered list op-code, we will allow for a popup of +/- keys
1525       // to create an ordered list of items
1526       //
1527       if (OrderedList) {
1528         if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1529             (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1530           //
1531           // Highlight reaches the bottom of the popup window, scroll one menu item.
1532           //
1533           TopOptionIndex++;
1534           ShowUpArrow = TRUE;
1535         }
1536 
1537         if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1538           ShowDownArrow = FALSE;
1539         }
1540 
1541         if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1542           HighlightOptionIndex++;
1543 
1544           ASSERT (CurrentOption != NULL);
1545           SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1546         }
1547       }
1548       break;
1549 
1550     case CHAR_NULL:
1551       switch (Key.ScanCode) {
1552       case SCAN_UP:
1553       case SCAN_DOWN:
1554         if (Key.ScanCode == SCAN_UP) {
1555           if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1556             //
1557             // Highlight reaches the top of the popup window, scroll one menu item.
1558             //
1559             TopOptionIndex--;
1560             ShowDownArrow = TRUE;
1561           }
1562 
1563           if (TopOptionIndex == 0) {
1564             ShowUpArrow = FALSE;
1565           }
1566 
1567           if (HighlightOptionIndex > 0) {
1568             HighlightOptionIndex--;
1569           }
1570         } else {
1571           if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1572               (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1573             //
1574             // Highlight reaches the bottom of the popup window, scroll one menu item.
1575             //
1576             TopOptionIndex++;
1577             ShowUpArrow = TRUE;
1578           }
1579 
1580           if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1581             ShowDownArrow = FALSE;
1582           }
1583 
1584           if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1585             HighlightOptionIndex++;
1586           }
1587         }
1588         break;
1589 
1590       case SCAN_ESC:
1591         gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1592 
1593         //
1594         // Restore link list order for orderedlist
1595         //
1596         if (OrderedList) {
1597           HiiValue.Type = ValueType;
1598           HiiValue.Value.u64 = 0;
1599           for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1600             HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1601             if (HiiValue.Value.u64 == 0) {
1602               break;
1603             }
1604 
1605             OneOfOption = ValueToOption (Question, &HiiValue);
1606             if (OneOfOption == NULL) {
1607               return EFI_NOT_FOUND;
1608             }
1609 
1610             RemoveEntryList (&OneOfOption->Link);
1611             InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1612           }
1613         }
1614 
1615         return EFI_DEVICE_ERROR;
1616 
1617       default:
1618         break;
1619       }
1620 
1621       break;
1622 
1623     case CHAR_CARRIAGE_RETURN:
1624       //
1625       // return the current selection
1626       //
1627       if (OrderedList) {
1628         ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
1629         ASSERT (ReturnValue != NULL);
1630         Index = 0;
1631         Link = GetFirstNode (&Question->OptionListHead);
1632         while (!IsNull (&Question->OptionListHead, Link)) {
1633           OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1634           Link = GetNextNode (&Question->OptionListHead, Link);
1635 
1636           SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
1637 
1638           Index++;
1639           if (Index > OrderList->MaxContainers) {
1640             break;
1641           }
1642         }
1643         if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
1644           FreePool (ReturnValue);
1645           return EFI_DEVICE_ERROR;
1646         } else {
1647           gUserInput->InputValue.Buffer = ReturnValue;
1648           gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1649         }
1650       } else {
1651         ASSERT (CurrentOption != NULL);
1652         gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
1653         if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
1654           return EFI_DEVICE_ERROR;
1655         } else {
1656           SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
1657         }
1658       }
1659 
1660       gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1661 
1662       return EFI_SUCCESS;
1663 
1664     default:
1665       break;
1666     }
1667   } while (TRUE);
1668 
1669 }
1670 
1671