Home | History | Annotate | Download | only in DisplayEngineDxe
      1 /** @file
      2 Implementation for handling user input from the User Interfaces.
      3 
      4 Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "FormDisplay.h"
     16 
     17 /**
     18   Get maximum and minimum info from this opcode.
     19 
     20   @param  OpCode            Pointer to the current input opcode.
     21   @param  Minimum           The minimum size info for this opcode.
     22   @param  Maximum           The maximum size info for this opcode.
     23 
     24 **/
     25 VOID
     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
     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
    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
    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
    480 GetNumericInput (
    481   IN  UI_MENU_OPTION              *MenuOption
    482   )
    483 {
    484   UINTN                   Column;
    485   UINTN                   Row;
    486   CHAR16                  InputText[MAX_NUMERIC_INPUT_WIDTH];
    487   CHAR16                  FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
    488   UINT64                  PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
    489   UINTN                   Count;
    490   UINTN                   Loop;
    491   BOOLEAN                 ManualInput;
    492   BOOLEAN                 HexInput;
    493   BOOLEAN                 IntInput;
    494   BOOLEAN                 Negative;
    495   BOOLEAN                 ValidateFail;
    496   BOOLEAN                 DateOrTime;
    497   UINTN                   InputWidth;
    498   UINT64                  EditValue;
    499   UINT64                  Step;
    500   UINT64                  Minimum;
    501   UINT64                  Maximum;
    502   UINTN                   EraseLen;
    503   UINT8                   Digital;
    504   EFI_INPUT_KEY           Key;
    505   EFI_HII_VALUE           *QuestionValue;
    506   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
    507   EFI_IFR_NUMERIC                *NumericOp;
    508   UINT16                         StorageWidth;
    509 
    510   Column            = MenuOption->OptCol;
    511   Row               = MenuOption->Row;
    512   PreviousNumber[0] = 0;
    513   Count             = 0;
    514   InputWidth        = 0;
    515   Digital           = 0;
    516   StorageWidth      = 0;
    517   Minimum           = 0;
    518   Maximum           = 0;
    519   NumericOp         = NULL;
    520   IntInput          = FALSE;
    521   HexInput          = FALSE;
    522   Negative          = FALSE;
    523   ValidateFail      = FALSE;
    524 
    525   Question      = MenuOption->ThisTag;
    526   QuestionValue = &Question->CurrentValue;
    527 
    528   //
    529   // Only two case, user can enter to this function: Enter and +/- case.
    530   // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
    531   //
    532   ManualInput        = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
    533 
    534   if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
    535     DateOrTime = TRUE;
    536   } else {
    537     DateOrTime = FALSE;
    538   }
    539 
    540   //
    541   // Prepare Value to be edit
    542   //
    543   EraseLen = 0;
    544   EditValue = 0;
    545   if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
    546     Step = 1;
    547     Minimum = 1;
    548 
    549     switch (MenuOption->Sequence) {
    550     case 0:
    551       Maximum = 12;
    552       EraseLen = 4;
    553       EditValue = QuestionValue->Value.date.Month;
    554       break;
    555 
    556     case 1:
    557       switch (QuestionValue->Value.date.Month) {
    558       case 2:
    559         if ((QuestionValue->Value.date.Year % 4) == 0  &&
    560             ((QuestionValue->Value.date.Year % 100) != 0 ||
    561             (QuestionValue->Value.date.Year % 400) == 0)) {
    562           Maximum = 29;
    563         } else {
    564           Maximum = 28;
    565         }
    566         break;
    567       case 4:
    568       case 6:
    569       case 9:
    570       case 11:
    571         Maximum = 30;
    572         break;
    573       default:
    574         Maximum = 31;
    575         break;
    576       }
    577 
    578       EraseLen = 3;
    579       EditValue = QuestionValue->Value.date.Day;
    580       break;
    581 
    582     case 2:
    583       Maximum = 0xffff;
    584       EraseLen = 5;
    585       EditValue = QuestionValue->Value.date.Year;
    586       break;
    587 
    588     default:
    589       break;
    590     }
    591   } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
    592     Step = 1;
    593     Minimum = 0;
    594 
    595     switch (MenuOption->Sequence) {
    596     case 0:
    597       Maximum = 23;
    598       EraseLen = 4;
    599       EditValue = QuestionValue->Value.time.Hour;
    600       break;
    601 
    602     case 1:
    603       Maximum = 59;
    604       EraseLen = 3;
    605       EditValue = QuestionValue->Value.time.Minute;
    606       break;
    607 
    608     case 2:
    609       Maximum = 59;
    610       EraseLen = 3;
    611       EditValue = QuestionValue->Value.time.Second;
    612       break;
    613 
    614     default:
    615       break;
    616     }
    617   } else {
    618     ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
    619     NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
    620     GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);
    621     EraseLen  = gOptionBlockWidth;
    622   }
    623 
    624   if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {
    625     if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){
    626       HexInput = TRUE;
    627     } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){
    628       //
    629       // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.
    630       //
    631       IntInput = TRUE;
    632     }
    633   }
    634 
    635   //
    636   // Enter from "Enter" input, clear the old word showing.
    637   //
    638   if (ManualInput) {
    639     if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
    640       if (HexInput) {
    641         InputWidth = StorageWidth * 2;
    642       } else {
    643         switch (StorageWidth) {
    644         case 1:
    645           InputWidth = 3;
    646           break;
    647 
    648         case 2:
    649           InputWidth = 5;
    650           break;
    651 
    652         case 4:
    653           InputWidth = 10;
    654           break;
    655 
    656         case 8:
    657           InputWidth = 20;
    658           break;
    659 
    660         default:
    661           InputWidth = 0;
    662           break;
    663         }
    664 
    665         if (IntInput) {
    666           //
    667           // Support an extra '-' for negative number.
    668           //
    669           InputWidth += 1;
    670         }
    671       }
    672 
    673       InputText[0] = LEFT_NUMERIC_DELIMITER;
    674       SetUnicodeMem (InputText + 1, InputWidth, L' ');
    675       ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
    676       InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
    677       InputText[InputWidth + 2] = L'\0';
    678 
    679       PrintStringAt (Column, Row, InputText);
    680       Column++;
    681     }
    682 
    683     if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
    684       if (MenuOption->Sequence == 2) {
    685         InputWidth = 4;
    686       } else {
    687         InputWidth = 2;
    688       }
    689 
    690       if (MenuOption->Sequence == 0) {
    691         InputText[0] = LEFT_NUMERIC_DELIMITER;
    692         SetUnicodeMem (InputText + 1, InputWidth, L' ');
    693       } else {
    694         SetUnicodeMem (InputText, InputWidth, L' ');
    695       }
    696 
    697       if (MenuOption->Sequence == 2) {
    698         InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
    699       } else {
    700         InputText[InputWidth + 1] = DATE_SEPARATOR;
    701       }
    702       InputText[InputWidth + 2] = L'\0';
    703 
    704       PrintStringAt (Column, Row, InputText);
    705       if (MenuOption->Sequence == 0) {
    706         Column++;
    707       }
    708     }
    709 
    710     if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
    711       InputWidth = 2;
    712 
    713       if (MenuOption->Sequence == 0) {
    714         InputText[0] = LEFT_NUMERIC_DELIMITER;
    715         SetUnicodeMem (InputText + 1, InputWidth, L' ');
    716       } else {
    717         SetUnicodeMem (InputText, InputWidth, L' ');
    718       }
    719 
    720       if (MenuOption->Sequence == 2) {
    721         InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
    722       } else {
    723         InputText[InputWidth + 1] = TIME_SEPARATOR;
    724       }
    725       InputText[InputWidth + 2] = L'\0';
    726 
    727       PrintStringAt (Column, Row, InputText);
    728       if (MenuOption->Sequence == 0) {
    729         Column++;
    730       }
    731     }
    732   }
    733 
    734   //
    735   // First time we enter this handler, we need to check to see if
    736   // we were passed an increment or decrement directive
    737   //
    738   do {
    739     Key.UnicodeChar = CHAR_NULL;
    740     if (gDirection != 0) {
    741       Key.ScanCode  = gDirection;
    742       gDirection    = 0;
    743       goto TheKey2;
    744     }
    745 
    746     WaitForKeyStroke (&Key);
    747 
    748 TheKey2:
    749     switch (Key.UnicodeChar) {
    750 
    751     case '+':
    752     case '-':
    753       if (ManualInput && IntInput) {
    754         //
    755         // In Manual input mode, check whether input the negative flag.
    756         //
    757         if (Key.UnicodeChar == '-') {
    758           if (Negative) {
    759             break;
    760           }
    761           Negative = TRUE;
    762           PrintCharAt (Column++, Row, Key.UnicodeChar);
    763         }
    764       } else {
    765         if (Key.UnicodeChar == '+') {
    766           Key.ScanCode = SCAN_RIGHT;
    767         } else {
    768           Key.ScanCode = SCAN_LEFT;
    769         }
    770         Key.UnicodeChar = CHAR_NULL;
    771         goto TheKey2;
    772       }
    773       break;
    774 
    775     case CHAR_NULL:
    776       switch (Key.ScanCode) {
    777       case SCAN_LEFT:
    778       case SCAN_RIGHT:
    779         if (DateOrTime && !ManualInput) {
    780           //
    781           // By setting this value, we will return back to the caller.
    782           // We need to do this since an auto-refresh will destroy the adjustment
    783           // based on what the real-time-clock is showing.  So we always commit
    784           // upon changing the value.
    785           //
    786           gDirection = SCAN_DOWN;
    787         }
    788 
    789         if ((Step != 0) && !ManualInput) {
    790           if (Key.ScanCode == SCAN_LEFT) {
    791             if (IntInput) {
    792               if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {
    793                 EditValue = EditValue - Step;
    794               } else if ((INT64) EditValue > (INT64) Minimum){
    795                 EditValue = Minimum;
    796               } else {
    797                 EditValue = Maximum;
    798               }
    799             } else {
    800               if (EditValue >= Minimum + Step) {
    801                 EditValue = EditValue - Step;
    802               } else if (EditValue > Minimum){
    803                 EditValue = Minimum;
    804               } else {
    805                 EditValue = Maximum;
    806               }
    807             }
    808           } else if (Key.ScanCode == SCAN_RIGHT) {
    809             if (IntInput) {
    810               if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {
    811                 EditValue = EditValue + Step;
    812               } else if ((INT64) EditValue < (INT64) Maximum) {
    813                 EditValue = Maximum;
    814               } else {
    815                 EditValue = Minimum;
    816               }
    817             } else {
    818               if (EditValue + Step <= Maximum) {
    819                 EditValue = EditValue + Step;
    820               } else if (EditValue < Maximum) {
    821                 EditValue = Maximum;
    822               } else {
    823                 EditValue = Minimum;
    824               }
    825             }
    826           }
    827 
    828           ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
    829           if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
    830             if (MenuOption->Sequence == 2) {
    831               //
    832               // Year
    833               //
    834               UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
    835             } else {
    836               //
    837               // Month/Day
    838               //
    839               UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
    840             }
    841 
    842             if (MenuOption->Sequence == 0) {
    843               ASSERT (EraseLen >= 2);
    844               FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
    845             } else if (MenuOption->Sequence == 1) {
    846               ASSERT (EraseLen >= 1);
    847               FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
    848             }
    849           } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
    850             UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
    851 
    852             if (MenuOption->Sequence == 0) {
    853               ASSERT (EraseLen >= 2);
    854               FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
    855             } else if (MenuOption->Sequence == 1) {
    856               ASSERT (EraseLen >= 1);
    857               FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
    858             }
    859           } else {
    860             QuestionValue->Value.u64 = EditValue;
    861             PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
    862           }
    863 
    864           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
    865           for (Loop = 0; Loop < EraseLen; Loop++) {
    866             PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
    867           }
    868           gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
    869 
    870           if (MenuOption->Sequence == 0) {
    871             PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
    872             Column = MenuOption->OptCol + 1;
    873           }
    874 
    875           PrintStringAt (Column, Row, FormattedNumber);
    876 
    877           if (!DateOrTime || MenuOption->Sequence == 2) {
    878             PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
    879           }
    880         }
    881 
    882         goto EnterCarriageReturn;
    883 
    884       case SCAN_UP:
    885       case SCAN_DOWN:
    886         goto EnterCarriageReturn;
    887 
    888       case SCAN_ESC:
    889         return EFI_DEVICE_ERROR;
    890 
    891       default:
    892         break;
    893       }
    894 
    895       break;
    896 
    897 EnterCarriageReturn:
    898 
    899     case CHAR_CARRIAGE_RETURN:
    900       //
    901       // Validate input value with Minimum value.
    902       //
    903       ValidateFail = FALSE;
    904       if (IntInput) {
    905         //
    906         // After user input Enter, need to check whether the input value.
    907         // If input a negative value, should compare with maximum value.
    908         // else compare with the minimum value.
    909         //
    910         if (Negative) {
    911           ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
    912         } else {
    913           ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
    914         }
    915 
    916         if (ValidateFail) {
    917           UpdateStatusBar (INPUT_ERROR, TRUE);
    918           break;
    919         }
    920       } else if (EditValue < Minimum) {
    921         UpdateStatusBar (INPUT_ERROR, TRUE);
    922         break;
    923       }
    924 
    925       UpdateStatusBar (INPUT_ERROR, FALSE);
    926       CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
    927       QuestionValue = &gUserInput->InputValue;
    928       //
    929       // Store Edit value back to Question
    930       //
    931       if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
    932         switch (MenuOption->Sequence) {
    933         case 0:
    934           QuestionValue->Value.date.Month = (UINT8) EditValue;
    935           break;
    936 
    937         case 1:
    938           QuestionValue->Value.date.Day = (UINT8) EditValue;
    939           break;
    940 
    941         case 2:
    942           QuestionValue->Value.date.Year = (UINT16) EditValue;
    943           break;
    944 
    945         default:
    946           break;
    947         }
    948       } else if (Question->OpCode->OpCode  == EFI_IFR_TIME_OP) {
    949         switch (MenuOption->Sequence) {
    950         case 0:
    951           QuestionValue->Value.time.Hour = (UINT8) EditValue;
    952           break;
    953 
    954         case 1:
    955           QuestionValue->Value.time.Minute = (UINT8) EditValue;
    956           break;
    957 
    958         case 2:
    959           QuestionValue->Value.time.Second = (UINT8) EditValue;
    960           break;
    961 
    962         default:
    963           break;
    964         }
    965       } else {
    966         //
    967         // Numeric
    968         //
    969         QuestionValue->Value.u64 = EditValue;
    970       }
    971 
    972       //
    973       // Adjust the value to the correct one.
    974       // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
    975       //              2013.03.29 -> 2013.02.29 -> 2013.02.28
    976       //
    977       if (Question->OpCode->OpCode  == EFI_IFR_DATE_OP &&
    978         (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
    979         AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
    980       }
    981 
    982       return EFI_SUCCESS;
    983 
    984     case CHAR_BACKSPACE:
    985       if (ManualInput) {
    986         if (Count == 0) {
    987           if (Negative) {
    988             Negative = FALSE;
    989             Column--;
    990             PrintStringAt (Column, Row, L" ");
    991           }
    992           break;
    993         }
    994         //
    995         // Remove a character
    996         //
    997         EditValue = PreviousNumber[Count - 1];
    998         UpdateStatusBar (INPUT_ERROR,  FALSE);
    999         Count--;
   1000         Column--;
   1001         PrintStringAt (Column, Row, L" ");
   1002       }
   1003       break;
   1004 
   1005     default:
   1006       if (ManualInput) {
   1007         if (HexInput) {
   1008           if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
   1009             Digital = (UINT8) (Key.UnicodeChar - L'0');
   1010           } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
   1011             Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
   1012           } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
   1013             Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
   1014           } else {
   1015             UpdateStatusBar (INPUT_ERROR, TRUE);
   1016             break;
   1017           }
   1018         } else {
   1019           if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
   1020             UpdateStatusBar (INPUT_ERROR, TRUE);
   1021             break;
   1022           }
   1023         }
   1024 
   1025         //
   1026         // If Count exceed input width, there is no way more is valid
   1027         //
   1028         if (Count >= InputWidth) {
   1029           break;
   1030         }
   1031         //
   1032         // Someone typed something valid!
   1033         //
   1034         if (Count != 0) {
   1035           if (HexInput) {
   1036             EditValue = LShiftU64 (EditValue, 4) + Digital;
   1037           } else if (IntInput && Negative) {
   1038             //
   1039             // Save the negative number.
   1040             //
   1041             EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;
   1042           } else {
   1043             EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
   1044           }
   1045         } else {
   1046           if (HexInput) {
   1047             EditValue = Digital;
   1048           } else if (IntInput && Negative) {
   1049             //
   1050             // Save the negative number.
   1051             //
   1052             EditValue = ~(Key.UnicodeChar - L'0') + 1;
   1053           } else {
   1054             EditValue = Key.UnicodeChar - L'0';
   1055           }
   1056         }
   1057 
   1058         if (IntInput) {
   1059           ValidateFail = FALSE;
   1060           //
   1061           // When user input a new value, should check the current value.
   1062           // If user input a negative value, should compare it with minimum
   1063           // value, else compare it with maximum value.
   1064           //
   1065           if (Negative) {
   1066             ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
   1067           } else {
   1068             ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
   1069           }
   1070 
   1071           if (ValidateFail) {
   1072             UpdateStatusBar (INPUT_ERROR, TRUE);
   1073             ASSERT (Count < ARRAY_SIZE (PreviousNumber));
   1074             EditValue = PreviousNumber[Count];
   1075             break;
   1076           }
   1077         } else {
   1078           if (EditValue > Maximum) {
   1079             UpdateStatusBar (INPUT_ERROR, TRUE);
   1080             ASSERT (Count < ARRAY_SIZE (PreviousNumber));
   1081             EditValue = PreviousNumber[Count];
   1082             break;
   1083           }
   1084         }
   1085 
   1086         UpdateStatusBar (INPUT_ERROR, FALSE);
   1087 
   1088         Count++;
   1089         ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));
   1090         PreviousNumber[Count] = EditValue;
   1091 
   1092         gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
   1093         PrintCharAt (Column, Row, Key.UnicodeChar);
   1094         Column++;
   1095       }
   1096       break;
   1097     }
   1098   } while (TRUE);
   1099 }
   1100 
   1101 /**
   1102   Adjust option order base on the question value.
   1103 
   1104   @param  Question           Pointer to current question.
   1105   @param  PopUpMenuLines     The line number of the pop up menu.
   1106 
   1107   @retval EFI_SUCCESS       If Option input is processed successfully
   1108   @retval EFI_DEVICE_ERROR  If operation fails
   1109 
   1110 **/
   1111 EFI_STATUS
   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
   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
   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
   1259 GetSelectionInputPopUp (
   1260   IN  UI_MENU_OPTION              *MenuOption
   1261   )
   1262 {
   1263   EFI_INPUT_KEY           Key;
   1264   UINTN                   Index;
   1265   CHAR16                  *StringPtr;
   1266   CHAR16                  *TempStringPtr;
   1267   UINTN                   Index2;
   1268   UINTN                   TopOptionIndex;
   1269   UINTN                   HighlightOptionIndex;
   1270   UINTN                   Start;
   1271   UINTN                   End;
   1272   UINTN                   Top;
   1273   UINTN                   Bottom;
   1274   UINTN                   PopUpMenuLines;
   1275   UINTN                   MenuLinesInView;
   1276   UINTN                   PopUpWidth;
   1277   CHAR16                  Character;
   1278   INT32                   SavedAttribute;
   1279   BOOLEAN                 ShowDownArrow;
   1280   BOOLEAN                 ShowUpArrow;
   1281   UINTN                   DimensionsWidth;
   1282   LIST_ENTRY              *Link;
   1283   BOOLEAN                 OrderedList;
   1284   UINT8                   *ValueArray;
   1285   UINT8                   *ReturnValue;
   1286   UINT8                   ValueType;
   1287   EFI_HII_VALUE           HiiValue;
   1288   DISPLAY_QUESTION_OPTION         *OneOfOption;
   1289   DISPLAY_QUESTION_OPTION         *CurrentOption;
   1290   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
   1291   INTN                    Result;
   1292   EFI_IFR_ORDERED_LIST    *OrderList;
   1293 
   1294   DimensionsWidth   = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
   1295 
   1296   ValueArray        = NULL;
   1297   ValueType         = 0;
   1298   CurrentOption     = NULL;
   1299   ShowDownArrow     = FALSE;
   1300   ShowUpArrow       = FALSE;
   1301 
   1302   ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
   1303 
   1304   Question = MenuOption->ThisTag;
   1305   if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
   1306     Link = GetFirstNode (&Question->OptionListHead);
   1307     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
   1308     ValueArray = Question->CurrentValue.Buffer;
   1309     ValueType =  OneOfOption->OptionOpCode->Type;
   1310     OrderedList = TRUE;
   1311     OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
   1312   } else {
   1313     OrderedList = FALSE;
   1314     OrderList = NULL;
   1315   }
   1316 
   1317   //
   1318   // Calculate Option count
   1319   //
   1320   PopUpMenuLines = 0;
   1321   if (OrderedList) {
   1322     AdjustOptionOrder(Question, &PopUpMenuLines);
   1323   } else {
   1324     Link = GetFirstNode (&Question->OptionListHead);
   1325     while (!IsNull (&Question->OptionListHead, Link)) {
   1326       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
   1327       PopUpMenuLines++;
   1328       Link = GetNextNode (&Question->OptionListHead, Link);
   1329     }
   1330   }
   1331 
   1332   //
   1333   // Get the number of one of options present and its size
   1334   //
   1335   PopUpWidth = 0;
   1336   HighlightOptionIndex = 0;
   1337   Link = GetFirstNode (&Question->OptionListHead);
   1338   for (Index = 0; Index < PopUpMenuLines; Index++) {
   1339     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
   1340 
   1341     StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
   1342     if (StrLen (StringPtr) > PopUpWidth) {
   1343       PopUpWidth = StrLen (StringPtr);
   1344     }
   1345     FreePool (StringPtr);
   1346     HiiValue.Type = OneOfOption->OptionOpCode->Type;
   1347     SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
   1348     if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
   1349       //
   1350       // Find current selected Option for OneOf
   1351       //
   1352       HighlightOptionIndex = Index;
   1353     }
   1354 
   1355     Link = GetNextNode (&Question->OptionListHead, Link);
   1356   }
   1357 
   1358   //
   1359   // Perform popup menu initialization.
   1360   //
   1361   PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
   1362 
   1363   SavedAttribute = gST->ConOut->Mode->Attribute;
   1364   gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
   1365 
   1366   if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
   1367     PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
   1368   }
   1369 
   1370   Start  = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
   1371   End    = Start + PopUpWidth + POPUP_FRAME_WIDTH;
   1372   Top    = gStatementDimensions.TopRow;
   1373   Bottom = gStatementDimensions.BottomRow - 1;
   1374 
   1375   MenuLinesInView = Bottom - Top - 1;
   1376   if (MenuLinesInView >= PopUpMenuLines) {
   1377     Top     = Top + (MenuLinesInView - PopUpMenuLines) / 2;
   1378     Bottom  = Top + PopUpMenuLines + 1;
   1379   } else {
   1380     ShowDownArrow = TRUE;
   1381   }
   1382 
   1383   if (HighlightOptionIndex > (MenuLinesInView - 1)) {
   1384     TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
   1385   } else {
   1386     TopOptionIndex = 0;
   1387   }
   1388 
   1389   do {
   1390     //
   1391     // Clear that portion of the screen
   1392     //
   1393     ClearLines (Start, End, Top, Bottom, GetPopupColor ());
   1394 
   1395     //
   1396     // Draw "One of" pop-up menu
   1397     //
   1398     Character = BOXDRAW_DOWN_RIGHT;
   1399     PrintCharAt (Start, Top, Character);
   1400     for (Index = Start; Index + 2 < End; Index++) {
   1401       if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
   1402         Character = GEOMETRICSHAPE_UP_TRIANGLE;
   1403       } else {
   1404         Character = BOXDRAW_HORIZONTAL;
   1405       }
   1406 
   1407       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
   1408     }
   1409 
   1410     Character = BOXDRAW_DOWN_LEFT;
   1411     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
   1412     Character = BOXDRAW_VERTICAL;
   1413     for (Index = Top + 1; Index < Bottom; Index++) {
   1414       PrintCharAt (Start, Index, Character);
   1415       PrintCharAt (End - 1, Index, Character);
   1416     }
   1417 
   1418     //
   1419     // Move to top Option
   1420     //
   1421     Link = GetFirstNode (&Question->OptionListHead);
   1422     for (Index = 0; Index < TopOptionIndex; Index++) {
   1423       Link = GetNextNode (&Question->OptionListHead, Link);
   1424     }
   1425 
   1426     //
   1427     // Display the One of options
   1428     //
   1429     Index2 = Top + 1;
   1430     for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
   1431       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
   1432       Link = GetNextNode (&Question->OptionListHead, Link);
   1433 
   1434       StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
   1435       ASSERT (StringPtr != NULL);
   1436       //
   1437       // If the string occupies multiple lines, truncate it to fit in one line,
   1438       // and append a "..." for indication.
   1439       //
   1440       if (StrLen (StringPtr) > (PopUpWidth - 1)) {
   1441         TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
   1442         ASSERT ( TempStringPtr != NULL );
   1443         CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
   1444         FreePool (StringPtr);
   1445         StringPtr = TempStringPtr;
   1446         StrCatS (StringPtr, PopUpWidth - 1, L"...");
   1447       }
   1448 
   1449       if (Index == HighlightOptionIndex) {
   1450           //
   1451           // Highlight the selected one
   1452           //
   1453           CurrentOption = OneOfOption;
   1454 
   1455           gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
   1456           PrintStringAt (Start + 2, Index2, StringPtr);
   1457           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
   1458         } else {
   1459           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
   1460           PrintStringAt (Start + 2, Index2, StringPtr);
   1461         }
   1462 
   1463       Index2++;
   1464       FreePool (StringPtr);
   1465     }
   1466 
   1467     Character = BOXDRAW_UP_RIGHT;
   1468     PrintCharAt (Start, Bottom, Character);
   1469     for (Index = Start; Index + 2 < End; Index++) {
   1470       if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
   1471         Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
   1472       } else {
   1473         Character = BOXDRAW_HORIZONTAL;
   1474       }
   1475 
   1476       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
   1477     }
   1478 
   1479     Character = BOXDRAW_UP_LEFT;
   1480     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
   1481 
   1482     //
   1483     // Get User selection
   1484     //
   1485     Key.UnicodeChar = CHAR_NULL;
   1486     if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
   1487       Key.ScanCode  = gDirection;
   1488       gDirection    = 0;
   1489       goto TheKey;
   1490     }
   1491 
   1492     WaitForKeyStroke (&Key);
   1493 
   1494 TheKey:
   1495     switch (Key.UnicodeChar) {
   1496     case '+':
   1497       if (OrderedList) {
   1498         if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
   1499           //
   1500           // Highlight reaches the top of the popup window, scroll one menu item.
   1501           //
   1502           TopOptionIndex--;
   1503           ShowDownArrow = TRUE;
   1504         }
   1505 
   1506         if (TopOptionIndex == 0) {
   1507           ShowUpArrow = FALSE;
   1508         }
   1509 
   1510         if (HighlightOptionIndex > 0) {
   1511           HighlightOptionIndex--;
   1512 
   1513           ASSERT (CurrentOption != NULL);
   1514           SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
   1515         }
   1516       }
   1517       break;
   1518 
   1519     case '-':
   1520       //
   1521       // If an ordered list op-code, we will allow for a popup of +/- keys
   1522       // to create an ordered list of items
   1523       //
   1524       if (OrderedList) {
   1525         if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
   1526             (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
   1527           //
   1528           // Highlight reaches the bottom of the popup window, scroll one menu item.
   1529           //
   1530           TopOptionIndex++;
   1531           ShowUpArrow = TRUE;
   1532         }
   1533 
   1534         if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
   1535           ShowDownArrow = FALSE;
   1536         }
   1537 
   1538         if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
   1539           HighlightOptionIndex++;
   1540 
   1541           ASSERT (CurrentOption != NULL);
   1542           SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
   1543         }
   1544       }
   1545       break;
   1546 
   1547     case CHAR_NULL:
   1548       switch (Key.ScanCode) {
   1549       case SCAN_UP:
   1550       case SCAN_DOWN:
   1551         if (Key.ScanCode == SCAN_UP) {
   1552           if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
   1553             //
   1554             // Highlight reaches the top of the popup window, scroll one menu item.
   1555             //
   1556             TopOptionIndex--;
   1557             ShowDownArrow = TRUE;
   1558           }
   1559 
   1560           if (TopOptionIndex == 0) {
   1561             ShowUpArrow = FALSE;
   1562           }
   1563 
   1564           if (HighlightOptionIndex > 0) {
   1565             HighlightOptionIndex--;
   1566           }
   1567         } else {
   1568           if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
   1569               (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
   1570             //
   1571             // Highlight reaches the bottom of the popup window, scroll one menu item.
   1572             //
   1573             TopOptionIndex++;
   1574             ShowUpArrow = TRUE;
   1575           }
   1576 
   1577           if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
   1578             ShowDownArrow = FALSE;
   1579           }
   1580 
   1581           if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
   1582             HighlightOptionIndex++;
   1583           }
   1584         }
   1585         break;
   1586 
   1587       case SCAN_ESC:
   1588         gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
   1589 
   1590         //
   1591         // Restore link list order for orderedlist
   1592         //
   1593         if (OrderedList) {
   1594           HiiValue.Type = ValueType;
   1595           HiiValue.Value.u64 = 0;
   1596           for (Index = 0; Index < OrderList->MaxContainers; Index++) {
   1597             HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
   1598             if (HiiValue.Value.u64 == 0) {
   1599               break;
   1600             }
   1601 
   1602             OneOfOption = ValueToOption (Question, &HiiValue);
   1603             if (OneOfOption == NULL) {
   1604               return EFI_NOT_FOUND;
   1605             }
   1606 
   1607             RemoveEntryList (&OneOfOption->Link);
   1608             InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
   1609           }
   1610         }
   1611 
   1612         return EFI_DEVICE_ERROR;
   1613 
   1614       default:
   1615         break;
   1616       }
   1617 
   1618       break;
   1619 
   1620     case CHAR_CARRIAGE_RETURN:
   1621       //
   1622       // return the current selection
   1623       //
   1624       if (OrderedList) {
   1625         ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
   1626         ASSERT (ReturnValue != NULL);
   1627         Index = 0;
   1628         Link = GetFirstNode (&Question->OptionListHead);
   1629         while (!IsNull (&Question->OptionListHead, Link)) {
   1630           OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
   1631           Link = GetNextNode (&Question->OptionListHead, Link);
   1632 
   1633           SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
   1634 
   1635           Index++;
   1636           if (Index > OrderList->MaxContainers) {
   1637             break;
   1638           }
   1639         }
   1640         if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
   1641           FreePool (ReturnValue);
   1642           return EFI_DEVICE_ERROR;
   1643         } else {
   1644           gUserInput->InputValue.Buffer = ReturnValue;
   1645           gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
   1646         }
   1647       } else {
   1648         ASSERT (CurrentOption != NULL);
   1649         gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
   1650         if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
   1651           return EFI_DEVICE_ERROR;
   1652         } else {
   1653           SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
   1654         }
   1655       }
   1656 
   1657       gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
   1658 
   1659       return EFI_SUCCESS;
   1660 
   1661     default:
   1662       break;
   1663     }
   1664   } while (TRUE);
   1665 
   1666 }
   1667 
   1668