1/** @file
2Implementation for handling user input from the User Interfaces.
3
4Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution.  The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
25VOID
26GetFieldFromOp (
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**/
59EFI_STATUS
60ReadString (
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**/
322VOID
323AdjustQuestionValue (
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**/
388VOID
389GetValueFromNum (
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**/
479EFI_STATUS
480GetNumericInput (
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
748TheKey2:
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
897EnterCarriageReturn:
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**/
1111EFI_STATUS
1112AdjustOptionOrder (
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**/
1180BOOLEAN
1181IsValuesEqual (
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**/
1215VOID
1216SetValuesByType (
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**/
1258EFI_STATUS
1259GetSelectionInputPopUp (
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
1494TheKey:
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