Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Eric Dong <eric.dong@intel.com> Reviewed-by: Liming, Gao <liming.gao@intel.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@15468 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			1532 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1532 lines
		
	
	
		
			45 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /** @file
 | |
| Implementation for handling user input from the User Interfaces.
 | |
| 
 | |
| Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
 | |
| This program and the accompanying materials
 | |
| are licensed and made available under the terms and conditions of the BSD License
 | |
| which accompanies this distribution.  The full text of the license may be found at
 | |
| http://opensource.org/licenses/bsd-license.php
 | |
| 
 | |
| THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
 | |
| WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
 | |
| 
 | |
| **/
 | |
| 
 | |
| #include "FormDisplay.h"
 | |
| 
 | |
| /**
 | |
|   Get maximum and minimum info from this opcode.
 | |
| 
 | |
|   @param  OpCode            Pointer to the current input opcode.
 | |
|   @param  Minimum           The minimum size info for this opcode.
 | |
|   @param  Maximum           The maximum size info for this opcode.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetFieldFromOp (
 | |
|   IN   EFI_IFR_OP_HEADER       *OpCode,
 | |
|   OUT  UINTN                   *Minimum,
 | |
|   OUT  UINTN                   *Maximum
 | |
|   )
 | |
| {
 | |
|   EFI_IFR_STRING    *StringOp;
 | |
|   EFI_IFR_PASSWORD  *PasswordOp;
 | |
|   if (OpCode->OpCode == EFI_IFR_STRING_OP) {
 | |
|     StringOp = (EFI_IFR_STRING *) OpCode;
 | |
|     *Minimum = StringOp->MinSize;
 | |
|     *Maximum = StringOp->MaxSize;    
 | |
|   } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
 | |
|     PasswordOp = (EFI_IFR_PASSWORD *) OpCode;
 | |
|     *Minimum = PasswordOp->MinSize;
 | |
|     *Maximum = PasswordOp->MaxSize;       
 | |
|   } else {
 | |
|     *Minimum = 0;
 | |
|     *Maximum = 0;       
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get string or password input from user.
 | |
| 
 | |
|   @param  MenuOption        Pointer to the current input menu.
 | |
|   @param  Prompt            The prompt string shown on popup window.
 | |
|   @param  StringPtr         Old user input and destination for use input string.
 | |
| 
 | |
|   @retval EFI_SUCCESS       If string input is read successfully
 | |
|   @retval EFI_DEVICE_ERROR  If operation fails
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| ReadString (
 | |
|   IN     UI_MENU_OPTION              *MenuOption,
 | |
|   IN     CHAR16                      *Prompt,
 | |
|   IN OUT CHAR16                      *StringPtr
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   EFI_INPUT_KEY           Key;
 | |
|   CHAR16                  NullCharacter;
 | |
|   UINTN                   ScreenSize;
 | |
|   CHAR16                  Space[2];
 | |
|   CHAR16                  KeyPad[2];
 | |
|   CHAR16                  *TempString;
 | |
|   CHAR16                  *BufferedString;
 | |
|   UINTN                   Index;
 | |
|   UINTN                   Index2;
 | |
|   UINTN                   Count;
 | |
|   UINTN                   Start;
 | |
|   UINTN                   Top;
 | |
|   UINTN                   DimensionsWidth;
 | |
|   UINTN                   DimensionsHeight;
 | |
|   UINTN                   CurrentCursor;
 | |
|   BOOLEAN                 CursorVisible;
 | |
|   UINTN                   Minimum;
 | |
|   UINTN                   Maximum;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   BOOLEAN                 IsPassword;
 | |
| 
 | |
|   DimensionsWidth  = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
 | |
|   DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;
 | |
| 
 | |
|   NullCharacter    = CHAR_NULL;
 | |
|   ScreenSize       = GetStringWidth (Prompt) / sizeof (CHAR16);
 | |
|   Space[0]         = L' ';
 | |
|   Space[1]         = CHAR_NULL;
 | |
| 
 | |
|   Question         = MenuOption->ThisTag;
 | |
|   GetFieldFromOp(Question->OpCode, &Minimum, &Maximum);
 | |
| 
 | |
|   if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) {
 | |
|     IsPassword = TRUE;
 | |
|   } else {
 | |
|     IsPassword = FALSE;
 | |
|   }
 | |
| 
 | |
|   TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));
 | |
|   ASSERT (TempString);
 | |
| 
 | |
|   if (ScreenSize < (Maximum + 1)) {
 | |
|     ScreenSize = Maximum + 1;
 | |
|   }
 | |
| 
 | |
|   if ((ScreenSize + 2) > DimensionsWidth) {
 | |
|     ScreenSize = DimensionsWidth - 2;
 | |
|   }
 | |
| 
 | |
|   BufferedString = AllocateZeroPool (ScreenSize * 2);
 | |
|   ASSERT (BufferedString);
 | |
| 
 | |
|   Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1;
 | |
|   Top   = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1;
 | |
| 
 | |
|   //
 | |
|   // Display prompt for string
 | |
|   //
 | |
|   // CreateDialog (NULL, "", Prompt, Space, "", NULL);
 | |
|   CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
 | |
|   gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
 | |
| 
 | |
|   CursorVisible = gST->ConOut->Mode->CursorVisible;
 | |
|   gST->ConOut->EnableCursor (gST->ConOut, TRUE);
 | |
| 
 | |
|   CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;
 | |
|   if (CurrentCursor != 0) {
 | |
|     //
 | |
|     // Show the string which has beed saved before.
 | |
|     //
 | |
|     SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
 | |
|     PrintStringAt (Start + 1, Top + 3, BufferedString);
 | |
| 
 | |
|     if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
 | |
|       Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
 | |
|     } else {
 | |
|       Index = 0;
 | |
|     }
 | |
| 
 | |
|     if (IsPassword) {
 | |
|       gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
 | |
|     }
 | |
| 
 | |
|     for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
 | |
|       BufferedString[Count] = StringPtr[Index];
 | |
| 
 | |
|       if (IsPassword) {
 | |
|         PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (!IsPassword) {
 | |
|       PrintStringAt (Start + 1, Top + 3, BufferedString);
 | |
|     }
 | |
|     
 | |
|     gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
 | |
|     gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
 | |
|   }
 | |
|   
 | |
|   do {
 | |
|     Status = WaitForKeyStroke (&Key);
 | |
|     ASSERT_EFI_ERROR (Status);
 | |
| 
 | |
|     gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
 | |
|     switch (Key.UnicodeChar) {
 | |
|     case CHAR_NULL:
 | |
|       switch (Key.ScanCode) {
 | |
|       case SCAN_LEFT:
 | |
|         if (CurrentCursor > 0) {
 | |
|           CurrentCursor--;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case SCAN_RIGHT:
 | |
|         if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {
 | |
|           CurrentCursor++;
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case SCAN_ESC:
 | |
|         FreePool (TempString);
 | |
|         FreePool (BufferedString);
 | |
|         gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
 | |
|         gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
 | |
|         return EFI_DEVICE_ERROR;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case CHAR_CARRIAGE_RETURN:
 | |
|       if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
 | |
| 
 | |
|         FreePool (TempString);
 | |
|         FreePool (BufferedString);
 | |
|         gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
 | |
|         gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
 | |
|         return EFI_SUCCESS;
 | |
|       } else {
 | |
|         //
 | |
|         // Simply create a popup to tell the user that they had typed in too few characters.
 | |
|         // To save code space, we can then treat this as an error and return back to the menu.
 | |
|         //
 | |
|         do {
 | |
|           CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL);
 | |
|         } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
 | |
| 
 | |
|         FreePool (TempString);
 | |
|         FreePool (BufferedString);
 | |
|         gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
 | |
|         gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
 | |
|         return EFI_DEVICE_ERROR;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case CHAR_BACKSPACE:
 | |
|       if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {
 | |
|         for (Index = 0; Index < CurrentCursor - 1; Index++) {
 | |
|           TempString[Index] = StringPtr[Index];
 | |
|         }
 | |
|         Count = GetStringWidth (StringPtr) / 2 - 1;
 | |
|         if (Count >= CurrentCursor) {
 | |
|           for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
 | |
|             TempString[Index] = StringPtr[Index2];
 | |
|           }
 | |
|           TempString[Index] = CHAR_NULL;
 | |
|         }
 | |
|         //
 | |
|         // Effectively truncate string by 1 character
 | |
|         //
 | |
|         StrCpy (StringPtr, TempString);
 | |
|         CurrentCursor --;
 | |
|       }
 | |
| 
 | |
|     default:
 | |
|       //
 | |
|       // If it is the beginning of the string, don't worry about checking maximum limits
 | |
|       //
 | |
|       if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
 | |
|         StrnCpy (StringPtr, &Key.UnicodeChar, 1);
 | |
|         CurrentCursor++;
 | |
|       } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
 | |
|         KeyPad[0] = Key.UnicodeChar;
 | |
|         KeyPad[1] = CHAR_NULL;
 | |
|         Count = GetStringWidth (StringPtr) / 2 - 1;
 | |
|         if (CurrentCursor < Count) {
 | |
|           for (Index = 0; Index < CurrentCursor; Index++) {
 | |
|             TempString[Index] = StringPtr[Index];
 | |
|           }
 | |
| 		  TempString[Index] = CHAR_NULL;
 | |
|           StrCat (TempString, KeyPad);
 | |
|           StrCat (TempString, StringPtr + CurrentCursor);
 | |
|           StrCpy (StringPtr, TempString);
 | |
|         } else {
 | |
|           StrCat (StringPtr, KeyPad);
 | |
|         }
 | |
|         CurrentCursor++;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // If the width of the input string is now larger than the screen, we nee to
 | |
|       // adjust the index to start printing portions of the string
 | |
|       //
 | |
|       SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
 | |
|       PrintStringAt (Start + 1, Top + 3, BufferedString);
 | |
| 
 | |
|       if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
 | |
|         Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
 | |
|       } else {
 | |
|         Index = 0;
 | |
|       }
 | |
| 
 | |
|       if (IsPassword) {
 | |
|         gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
 | |
|       }
 | |
| 
 | |
|       for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
 | |
|         BufferedString[Count] = StringPtr[Index];
 | |
| 
 | |
|         if (IsPassword) {
 | |
|           PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       if (!IsPassword) {
 | |
|         PrintStringAt (Start + 1, Top + 3, BufferedString);
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
 | |
|     gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
 | |
|   } while (TRUE);
 | |
| 
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Adjust the value to the correct one. Rules follow the sample:
 | |
|   like:  Year change:  2012.02.29 -> 2013.02.29 -> 2013.02.01
 | |
|          Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
 | |
| 
 | |
|   @param  QuestionValue     Pointer to current question.
 | |
|   @param  Sequence          The sequence of the field in the question.
 | |
| **/
 | |
| VOID
 | |
| AdjustQuestionValue (
 | |
|   IN  EFI_HII_VALUE           *QuestionValue,
 | |
|   IN  UINT8                   Sequence
 | |
|   )
 | |
| {
 | |
|   UINT8     Month;
 | |
|   UINT16    Year;
 | |
|   UINT8     Maximum;
 | |
|   UINT8     Minimum;
 | |
| 
 | |
|   Month   = QuestionValue->Value.date.Month;
 | |
|   Year    = QuestionValue->Value.date.Year;
 | |
|   Minimum = 1;
 | |
| 
 | |
|   switch (Month) {
 | |
|   case 2:
 | |
|     if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {
 | |
|       Maximum = 29;
 | |
|     } else {
 | |
|       Maximum = 28;
 | |
|     }
 | |
|     break;
 | |
|   case 4:
 | |
|   case 6:
 | |
|   case 9:
 | |
|   case 11:
 | |
|     Maximum = 30;
 | |
|     break;
 | |
|   default:
 | |
|     Maximum = 31;
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Change the month area.
 | |
|   //
 | |
|   if (Sequence == 0) {
 | |
|     if (QuestionValue->Value.date.Day > Maximum) {
 | |
|       QuestionValue->Value.date.Day = Maximum;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   //
 | |
|   // Change the Year area.
 | |
|   //
 | |
|   if (Sequence == 2) {
 | |
|     if (QuestionValue->Value.date.Day > Maximum) {
 | |
|       QuestionValue->Value.date.Day = Minimum;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get field info from numeric opcode.
 | |
| 
 | |
|   @param  OpCode            Pointer to the current input opcode.
 | |
|   @param  Minimum           The minimum size info for this opcode.
 | |
|   @param  Maximum           The maximum size info for this opcode.
 | |
|   @param  Step              The step size info for this opcode.
 | |
|   @param  StorageWidth      The storage width info for this opcode.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| GetValueFromNum (
 | |
|   IN  EFI_IFR_OP_HEADER     *OpCode,
 | |
|   OUT UINT64                *Minimum,
 | |
|   OUT UINT64                *Maximum,
 | |
|   OUT UINT64                *Step,
 | |
|   OUT UINT16                *StorageWidth
 | |
| )
 | |
| {
 | |
|   EFI_IFR_NUMERIC       *NumericOp;
 | |
| 
 | |
|   NumericOp = (EFI_IFR_NUMERIC *) OpCode;
 | |
|   
 | |
|   switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
 | |
|   case EFI_IFR_NUMERIC_SIZE_1:
 | |
|     *Minimum = NumericOp->data.u8.MinValue;
 | |
|     *Maximum = NumericOp->data.u8.MaxValue;
 | |
|     *Step    = NumericOp->data.u8.Step;
 | |
|     *StorageWidth = (UINT16) sizeof (UINT8);
 | |
|     break;
 | |
|   
 | |
|   case EFI_IFR_NUMERIC_SIZE_2:
 | |
|     *Minimum = NumericOp->data.u16.MinValue;
 | |
|     *Maximum = NumericOp->data.u16.MaxValue;
 | |
|     *Step    = NumericOp->data.u16.Step;
 | |
|     *StorageWidth = (UINT16) sizeof (UINT16);
 | |
|     break;
 | |
|   
 | |
|   case EFI_IFR_NUMERIC_SIZE_4:
 | |
|     *Minimum = NumericOp->data.u32.MinValue;
 | |
|     *Maximum = NumericOp->data.u32.MaxValue;
 | |
|     *Step    = NumericOp->data.u32.Step;
 | |
|     *StorageWidth = (UINT16) sizeof (UINT32);
 | |
|     break;
 | |
|   
 | |
|   case EFI_IFR_NUMERIC_SIZE_8:
 | |
|     *Minimum = NumericOp->data.u64.MinValue;
 | |
|     *Maximum = NumericOp->data.u64.MaxValue;
 | |
|     *Step    = NumericOp->data.u64.Step;
 | |
|     *StorageWidth = (UINT16) sizeof (UINT64);
 | |
|     break;
 | |
|   
 | |
|   default:
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   if (*Maximum == 0) {
 | |
|     *Maximum = (UINT64) -1;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   This routine reads a numeric value from the user input.
 | |
| 
 | |
|   @param  MenuOption        Pointer to the current input menu.
 | |
| 
 | |
|   @retval EFI_SUCCESS       If numerical input is read successfully
 | |
|   @retval EFI_DEVICE_ERROR  If operation fails
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| GetNumericInput (
 | |
|   IN  UI_MENU_OPTION              *MenuOption
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   UINTN                   Column;
 | |
|   UINTN                   Row;
 | |
|   CHAR16                  InputText[MAX_NUMERIC_INPUT_WIDTH];
 | |
|   CHAR16                  FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
 | |
|   UINT64                  PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
 | |
|   UINTN                   Count;
 | |
|   UINTN                   Loop;
 | |
|   BOOLEAN                 ManualInput;
 | |
|   BOOLEAN                 HexInput;
 | |
|   BOOLEAN                 DateOrTime;
 | |
|   UINTN                   InputWidth;
 | |
|   UINT64                  EditValue;
 | |
|   UINT64                  Step;
 | |
|   UINT64                  Minimum;
 | |
|   UINT64                  Maximum;
 | |
|   UINTN                   EraseLen;
 | |
|   UINT8                   Digital;
 | |
|   EFI_INPUT_KEY           Key;
 | |
|   EFI_HII_VALUE           *QuestionValue;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   EFI_IFR_NUMERIC                *NumericOp;
 | |
|   UINT16                         StorageWidth;
 | |
| 
 | |
|   Column            = MenuOption->OptCol;
 | |
|   Row               = MenuOption->Row;
 | |
|   PreviousNumber[0] = 0;
 | |
|   Count             = 0;
 | |
|   InputWidth        = 0;
 | |
|   Digital           = 0;
 | |
|   StorageWidth      = 0;
 | |
|   Minimum           = 0;
 | |
|   Maximum           = 0;
 | |
|   NumericOp         = NULL;
 | |
| 
 | |
|   Question      = MenuOption->ThisTag;
 | |
|   QuestionValue = &Question->CurrentValue;
 | |
| 
 | |
|   //
 | |
|   // Only two case, user can enter to this function: Enter and +/- case.
 | |
|   // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
 | |
|   //
 | |
|   ManualInput        = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
 | |
| 
 | |
|   if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
 | |
|     DateOrTime = TRUE;
 | |
|   } else {
 | |
|     DateOrTime = FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Prepare Value to be edit
 | |
|   //
 | |
|   EraseLen = 0;
 | |
|   EditValue = 0;
 | |
|   if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
 | |
|     Step = 1;
 | |
|     Minimum = 1;
 | |
| 
 | |
|     switch (MenuOption->Sequence) {
 | |
|     case 0:
 | |
|       Maximum = 12;
 | |
|       EraseLen = 4;
 | |
|       EditValue = QuestionValue->Value.date.Month;
 | |
|       break;
 | |
| 
 | |
|     case 1:
 | |
|       switch (QuestionValue->Value.date.Month) {
 | |
|       case 2:
 | |
|         if ((QuestionValue->Value.date.Year % 4) == 0  && 
 | |
|             ((QuestionValue->Value.date.Year % 100) != 0 || 
 | |
|             (QuestionValue->Value.date.Year % 400) == 0)) {
 | |
|           Maximum = 29;
 | |
|         } else {
 | |
|           Maximum = 28;
 | |
|         }
 | |
|         break;
 | |
|       case 4:
 | |
|       case 6:
 | |
|       case 9:
 | |
|       case 11:
 | |
|         Maximum = 30;
 | |
|         break;
 | |
|       default:
 | |
|         Maximum = 31;
 | |
|         break;
 | |
|       } 
 | |
| 
 | |
|       EraseLen = 3;
 | |
|       EditValue = QuestionValue->Value.date.Day;
 | |
|       break;
 | |
| 
 | |
|     case 2:
 | |
|       Maximum = 0xffff;
 | |
|       EraseLen = 5;
 | |
|       EditValue = QuestionValue->Value.date.Year;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
 | |
|     Step = 1;
 | |
|     Minimum = 0;
 | |
| 
 | |
|     switch (MenuOption->Sequence) {
 | |
|     case 0:
 | |
|       Maximum = 23;
 | |
|       EraseLen = 4;
 | |
|       EditValue = QuestionValue->Value.time.Hour;
 | |
|       break;
 | |
| 
 | |
|     case 1:
 | |
|       Maximum = 59;
 | |
|       EraseLen = 3;
 | |
|       EditValue = QuestionValue->Value.time.Minute;
 | |
|       break;
 | |
| 
 | |
|     case 2:
 | |
|       Maximum = 59;
 | |
|       EraseLen = 3;
 | |
|       EditValue = QuestionValue->Value.time.Second;
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   } else {
 | |
|     ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
 | |
|     NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
 | |
|     GetValueFromNum(Question->OpCode, &Minimum, &Maximum, &Step, &StorageWidth);
 | |
|     EditValue = QuestionValue->Value.u64;
 | |
|     EraseLen  = gOptionBlockWidth;
 | |
|   }
 | |
| 
 | |
|   if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL) &&
 | |
|       ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
 | |
|     HexInput = TRUE;
 | |
|   } else {
 | |
|     HexInput = FALSE;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Enter from "Enter" input, clear the old word showing.
 | |
|   //
 | |
|   if (ManualInput) {
 | |
|     if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
 | |
|       if (HexInput) {
 | |
|         InputWidth = StorageWidth * 2;
 | |
|       } else {
 | |
|         switch (StorageWidth) {
 | |
|         case 1:
 | |
|           InputWidth = 3;
 | |
|           break;
 | |
| 
 | |
|         case 2:
 | |
|           InputWidth = 5;
 | |
|           break;
 | |
| 
 | |
|         case 4:
 | |
|           InputWidth = 10;
 | |
|           break;
 | |
| 
 | |
|         case 8:
 | |
|           InputWidth = 20;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           InputWidth = 0;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       InputText[0] = LEFT_NUMERIC_DELIMITER;
 | |
|       SetUnicodeMem (InputText + 1, InputWidth, L' ');
 | |
|       ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
 | |
|       InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
 | |
|       InputText[InputWidth + 2] = L'\0';
 | |
| 
 | |
|       PrintStringAt (Column, Row, InputText);
 | |
|       Column++;
 | |
|     }
 | |
| 
 | |
|     if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
 | |
|       if (MenuOption->Sequence == 2) {
 | |
|         InputWidth = 4;
 | |
|       } else {
 | |
|         InputWidth = 2;
 | |
|       }
 | |
| 
 | |
|       if (MenuOption->Sequence == 0) {
 | |
|         InputText[0] = LEFT_NUMERIC_DELIMITER;
 | |
|         SetUnicodeMem (InputText + 1, InputWidth, L' ');
 | |
|       } else {
 | |
|         SetUnicodeMem (InputText, InputWidth, L' ');
 | |
|       }
 | |
| 
 | |
|       if (MenuOption->Sequence == 2) {
 | |
|         InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
 | |
|       } else {
 | |
|         InputText[InputWidth + 1] = DATE_SEPARATOR;
 | |
|       }
 | |
|       InputText[InputWidth + 2] = L'\0';
 | |
| 
 | |
|       PrintStringAt (Column, Row, InputText);
 | |
|       if (MenuOption->Sequence == 0) {
 | |
|         Column++;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
 | |
|       InputWidth = 2;
 | |
| 
 | |
|       if (MenuOption->Sequence == 0) {
 | |
|         InputText[0] = LEFT_NUMERIC_DELIMITER;
 | |
|         SetUnicodeMem (InputText + 1, InputWidth, L' ');
 | |
|       } else {
 | |
|         SetUnicodeMem (InputText, InputWidth, L' ');
 | |
|       }
 | |
| 
 | |
|       if (MenuOption->Sequence == 2) {
 | |
|         InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
 | |
|       } else {
 | |
|         InputText[InputWidth + 1] = TIME_SEPARATOR;
 | |
|       }
 | |
|       InputText[InputWidth + 2] = L'\0';
 | |
| 
 | |
|       PrintStringAt (Column, Row, InputText);
 | |
|       if (MenuOption->Sequence == 0) {
 | |
|         Column++;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // First time we enter this handler, we need to check to see if
 | |
|   // we were passed an increment or decrement directive
 | |
|   //
 | |
|   do {
 | |
|     Key.UnicodeChar = CHAR_NULL;
 | |
|     if (gDirection != 0) {
 | |
|       Key.ScanCode  = gDirection;
 | |
|       gDirection    = 0;
 | |
|       goto TheKey2;
 | |
|     }
 | |
| 
 | |
|     Status = WaitForKeyStroke (&Key);
 | |
| 
 | |
| TheKey2:
 | |
|     switch (Key.UnicodeChar) {
 | |
| 
 | |
|     case '+':
 | |
|     case '-':
 | |
|       if (Key.UnicodeChar == '+') {
 | |
|         Key.ScanCode = SCAN_RIGHT;
 | |
|       } else {
 | |
|         Key.ScanCode = SCAN_LEFT;
 | |
|       }
 | |
|       Key.UnicodeChar = CHAR_NULL;
 | |
|       goto TheKey2;
 | |
| 
 | |
|     case CHAR_NULL:
 | |
|       switch (Key.ScanCode) {
 | |
|       case SCAN_LEFT:
 | |
|       case SCAN_RIGHT:
 | |
|         if (DateOrTime && !ManualInput) {
 | |
|           //
 | |
|           // By setting this value, we will return back to the caller.
 | |
|           // We need to do this since an auto-refresh will destroy the adjustment
 | |
|           // based on what the real-time-clock is showing.  So we always commit
 | |
|           // upon changing the value.
 | |
|           //
 | |
|           gDirection = SCAN_DOWN;
 | |
|         }
 | |
| 
 | |
|         if ((Step != 0) && !ManualInput) {
 | |
|           if (Key.ScanCode == SCAN_LEFT) {
 | |
|             if (EditValue >= Minimum + Step) {
 | |
|               EditValue = EditValue - Step;
 | |
|             } else if (EditValue > Minimum){
 | |
|               EditValue = Minimum;
 | |
|             } else {
 | |
|               EditValue = Maximum;
 | |
|             }
 | |
|           } else if (Key.ScanCode == SCAN_RIGHT) {
 | |
|             if (EditValue + Step <= Maximum) {
 | |
|               EditValue = EditValue + Step;
 | |
|             } else if (EditValue < Maximum) {
 | |
|               EditValue = Maximum;
 | |
|             } else {
 | |
|               EditValue = Minimum;
 | |
|             }
 | |
|           }
 | |
| 
 | |
|           ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
 | |
|           if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
 | |
|             if (MenuOption->Sequence == 2) {
 | |
|               //
 | |
|               // Year
 | |
|               //
 | |
|               UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
 | |
|             } else {
 | |
|               //
 | |
|               // Month/Day
 | |
|               //
 | |
|               UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
 | |
|             }
 | |
| 
 | |
|             if (MenuOption->Sequence == 0) {
 | |
|               ASSERT (EraseLen >= 2);
 | |
|               FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
 | |
|             } else if (MenuOption->Sequence == 1) {
 | |
|               ASSERT (EraseLen >= 1);
 | |
|               FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
 | |
|             }
 | |
|           } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
 | |
|             UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
 | |
| 
 | |
|             if (MenuOption->Sequence == 0) {
 | |
|               ASSERT (EraseLen >= 2);
 | |
|               FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
 | |
|             } else if (MenuOption->Sequence == 1) {
 | |
|               ASSERT (EraseLen >= 1);
 | |
|               FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
 | |
|             }
 | |
|           } else {
 | |
|             QuestionValue->Value.u64 = EditValue;
 | |
|             PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
 | |
|           }
 | |
| 
 | |
|           gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
 | |
|           for (Loop = 0; Loop < EraseLen; Loop++) {
 | |
|             PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
 | |
|           }
 | |
|           gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
 | |
| 
 | |
|           if (MenuOption->Sequence == 0) {
 | |
|             PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
 | |
|             Column = MenuOption->OptCol + 1;
 | |
|           }
 | |
| 
 | |
|           PrintStringAt (Column, Row, FormattedNumber);
 | |
| 
 | |
|           if (!DateOrTime || MenuOption->Sequence == 2) {
 | |
|             PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         goto EnterCarriageReturn;
 | |
|         break;
 | |
| 
 | |
|       case SCAN_UP:
 | |
|       case SCAN_DOWN:
 | |
|         goto EnterCarriageReturn;
 | |
| 
 | |
|       case SCAN_ESC:
 | |
|         return EFI_DEVICE_ERROR;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
| EnterCarriageReturn:
 | |
| 
 | |
|     case CHAR_CARRIAGE_RETURN:
 | |
|       //
 | |
|       // Validate input value with Minimum value.
 | |
|       //
 | |
|       if (EditValue < Minimum) {
 | |
|         UpdateStatusBar (INPUT_ERROR, TRUE);
 | |
|         break;
 | |
|       } else {
 | |
|         UpdateStatusBar (INPUT_ERROR, FALSE);
 | |
|       }
 | |
|       
 | |
|       CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
 | |
|       QuestionValue = &gUserInput->InputValue;
 | |
|       //
 | |
|       // Store Edit value back to Question
 | |
|       //
 | |
|       if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
 | |
|         switch (MenuOption->Sequence) {
 | |
|         case 0:
 | |
|           QuestionValue->Value.date.Month = (UINT8) EditValue;
 | |
|           break;
 | |
| 
 | |
|         case 1:
 | |
|           QuestionValue->Value.date.Day = (UINT8) EditValue;
 | |
|           break;
 | |
| 
 | |
|         case 2:
 | |
|           QuestionValue->Value.date.Year = (UINT16) EditValue;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           break;
 | |
|         }
 | |
|       } else if (Question->OpCode->OpCode  == EFI_IFR_TIME_OP) {
 | |
|         switch (MenuOption->Sequence) {
 | |
|         case 0:
 | |
|           QuestionValue->Value.time.Hour = (UINT8) EditValue;
 | |
|           break;
 | |
| 
 | |
|         case 1:
 | |
|           QuestionValue->Value.time.Minute = (UINT8) EditValue;
 | |
|           break;
 | |
| 
 | |
|         case 2:
 | |
|           QuestionValue->Value.time.Second = (UINT8) EditValue;
 | |
|           break;
 | |
| 
 | |
|         default:
 | |
|           break;
 | |
|         }
 | |
|       } else {
 | |
|         //
 | |
|         // Numeric
 | |
|         //
 | |
|         QuestionValue->Value.u64 = EditValue;
 | |
|       }
 | |
| 
 | |
|       //
 | |
|       // Adjust the value to the correct one.
 | |
|       // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
 | |
|       //              2013.03.29 -> 2013.02.29 -> 2013.02.28
 | |
|       //
 | |
|       if (Question->OpCode->OpCode  == EFI_IFR_DATE_OP && 
 | |
|         (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
 | |
|         AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
 | |
|       }
 | |
| 
 | |
|       return EFI_SUCCESS;
 | |
|       break;
 | |
| 
 | |
|     case CHAR_BACKSPACE:
 | |
|       if (ManualInput) {
 | |
|         if (Count == 0) {
 | |
|           break;
 | |
|         }
 | |
|         //
 | |
|         // Remove a character
 | |
|         //
 | |
|         EditValue = PreviousNumber[Count - 1];
 | |
|         UpdateStatusBar (INPUT_ERROR,  FALSE);
 | |
|         Count--;
 | |
|         Column--;
 | |
|         PrintStringAt (Column, Row, L" ");
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       if (ManualInput) {
 | |
|         if (HexInput) {
 | |
|           if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
 | |
|             Digital = (UINT8) (Key.UnicodeChar - L'0');
 | |
|           } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
 | |
|             Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
 | |
|           } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
 | |
|             Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
 | |
|           } else {
 | |
|             UpdateStatusBar (INPUT_ERROR, TRUE);
 | |
|             break;
 | |
|           }
 | |
|         } else {
 | |
|           if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
 | |
|             UpdateStatusBar (INPUT_ERROR, TRUE);
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         //
 | |
|         // If Count exceed input width, there is no way more is valid
 | |
|         //
 | |
|         if (Count >= InputWidth) {
 | |
|           break;
 | |
|         }
 | |
|         //
 | |
|         // Someone typed something valid!
 | |
|         //
 | |
|         if (Count != 0) {
 | |
|           if (HexInput) {
 | |
|             EditValue = LShiftU64 (EditValue, 4) + Digital;
 | |
|           } else {
 | |
|             EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
 | |
|           }
 | |
|         } else {
 | |
|           if (HexInput) {
 | |
|             EditValue = Digital;
 | |
|           } else {
 | |
|             EditValue = Key.UnicodeChar - L'0';
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         if (EditValue > Maximum) {
 | |
|           UpdateStatusBar (INPUT_ERROR, TRUE);
 | |
|           ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
 | |
|           EditValue = PreviousNumber[Count];
 | |
|           break;
 | |
|         } else {
 | |
|           UpdateStatusBar (INPUT_ERROR, FALSE);
 | |
|         }
 | |
| 
 | |
|         Count++;
 | |
|         ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
 | |
|         PreviousNumber[Count] = EditValue;
 | |
| 
 | |
|         gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
 | |
|         PrintCharAt (Column, Row, Key.UnicodeChar);
 | |
|         Column++;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|   } while (TRUE);
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Adjust option order base on the question value.
 | |
| 
 | |
|   @param  Question           Pointer to current question.
 | |
|   @param  PopUpMenuLines     The line number of the pop up menu.
 | |
| 
 | |
|   @retval EFI_SUCCESS       If Option input is processed successfully
 | |
|   @retval EFI_DEVICE_ERROR  If operation fails
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| AdjustOptionOrder (
 | |
|   IN  FORM_DISPLAY_ENGINE_STATEMENT  *Question,
 | |
|   OUT UINTN                          *PopUpMenuLines
 | |
|   )
 | |
| {
 | |
|   UINTN                   Index;
 | |
|   EFI_IFR_ORDERED_LIST    *OrderList;
 | |
|   UINT8                   *ValueArray;
 | |
|   UINT8                   ValueType;
 | |
|   LIST_ENTRY              *Link;
 | |
|   DISPLAY_QUESTION_OPTION *OneOfOption;
 | |
|   EFI_HII_VALUE           *HiiValueArray;
 | |
| 
 | |
|   Link        = GetFirstNode (&Question->OptionListHead);
 | |
|   OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|   ValueArray  = Question->CurrentValue.Buffer;
 | |
|   ValueType   =  OneOfOption->OptionOpCode->Type;
 | |
|   OrderList   = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
 | |
| 
 | |
|   for (Index = 0; Index < OrderList->MaxContainers; Index++) {
 | |
|     if (GetArrayData (ValueArray, ValueType, Index) == 0) {
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   
 | |
|   *PopUpMenuLines = Index;
 | |
|   
 | |
|   //
 | |
|   // Prepare HiiValue array
 | |
|   //  
 | |
|   HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
 | |
|   ASSERT (HiiValueArray != NULL);
 | |
| 
 | |
|   for (Index = 0; Index < *PopUpMenuLines; Index++) {
 | |
|     HiiValueArray[Index].Type = ValueType;
 | |
|     HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
 | |
|   }
 | |
|   
 | |
|   for (Index = 0; Index < *PopUpMenuLines; Index++) {
 | |
|     OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
 | |
|     if (OneOfOption == NULL) {
 | |
|       return EFI_NOT_FOUND;
 | |
|     }
 | |
|   
 | |
|     RemoveEntryList (&OneOfOption->Link);
 | |
|   
 | |
|     //
 | |
|     // Insert to head.
 | |
|     //
 | |
|     InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
 | |
|   }
 | |
|   
 | |
|   FreePool (HiiValueArray);
 | |
| 
 | |
|   return EFI_SUCCESS;
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Base on the type to compare the value.
 | |
| 
 | |
|   @param  Value1                The first value need to compare.
 | |
|   @param  Value2                The second value need to compare.
 | |
|   @param  Type                  The value type for above two values.
 | |
| 
 | |
|   @retval TRUE                  The two value are same.
 | |
|   @retval FALSE                 The two value are different.
 | |
| 
 | |
| **/
 | |
| BOOLEAN
 | |
| IsValuesEqual (
 | |
|   IN EFI_IFR_TYPE_VALUE *Value1,
 | |
|   IN EFI_IFR_TYPE_VALUE *Value2,
 | |
|   IN UINT8              Type
 | |
|   )
 | |
| {
 | |
|   switch (Type) {
 | |
|   case EFI_IFR_TYPE_BOOLEAN:
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|     return (BOOLEAN) (Value1->u8 == Value2->u8);
 | |
|   
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|     return (BOOLEAN) (Value1->u16 == Value2->u16);
 | |
|   
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|     return (BOOLEAN) (Value1->u32 == Value2->u32);
 | |
|   
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|     return (BOOLEAN) (Value1->u64 == Value2->u64);
 | |
| 
 | |
|   default:
 | |
|     ASSERT (FALSE);
 | |
|     return FALSE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Base on the type to set the value.
 | |
| 
 | |
|   @param  Dest                  The dest value.
 | |
|   @param  Source                The source value.
 | |
|   @param  Type                  The value type for above two values.
 | |
| 
 | |
| **/
 | |
| VOID
 | |
| SetValuesByType (
 | |
|   OUT EFI_IFR_TYPE_VALUE *Dest,
 | |
|   IN  EFI_IFR_TYPE_VALUE *Source,
 | |
|   IN  UINT8              Type
 | |
|   )
 | |
| {
 | |
|   switch (Type) {
 | |
|   case EFI_IFR_TYPE_BOOLEAN:
 | |
|     Dest->b = Source->b;
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_8:
 | |
|     Dest->u8 = Source->u8;
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_16:
 | |
|     Dest->u16 = Source->u16;
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_32:
 | |
|     Dest->u32 = Source->u32;
 | |
|     break;
 | |
| 
 | |
|   case EFI_IFR_TYPE_NUM_SIZE_64:
 | |
|     Dest->u64 = Source->u64;
 | |
|     break;
 | |
| 
 | |
|   default:
 | |
|     ASSERT (FALSE);
 | |
|     break;
 | |
|   }
 | |
| }
 | |
| 
 | |
| /**
 | |
|   Get selection for OneOf and OrderedList (Left/Right will be ignored).
 | |
| 
 | |
|   @param  MenuOption        Pointer to the current input menu.
 | |
| 
 | |
|   @retval EFI_SUCCESS       If Option input is processed successfully
 | |
|   @retval EFI_DEVICE_ERROR  If operation fails
 | |
| 
 | |
| **/
 | |
| EFI_STATUS
 | |
| GetSelectionInputPopUp (
 | |
|   IN  UI_MENU_OPTION              *MenuOption
 | |
|   )
 | |
| {
 | |
|   EFI_STATUS              Status;
 | |
|   EFI_INPUT_KEY           Key;
 | |
|   UINTN                   Index;
 | |
|   CHAR16                  *StringPtr;
 | |
|   CHAR16                  *TempStringPtr;
 | |
|   UINTN                   Index2;
 | |
|   UINTN                   TopOptionIndex;
 | |
|   UINTN                   HighlightOptionIndex;
 | |
|   UINTN                   Start;
 | |
|   UINTN                   End;
 | |
|   UINTN                   Top;
 | |
|   UINTN                   Bottom;
 | |
|   UINTN                   PopUpMenuLines;
 | |
|   UINTN                   MenuLinesInView;
 | |
|   UINTN                   PopUpWidth;
 | |
|   CHAR16                  Character;
 | |
|   INT32                   SavedAttribute;
 | |
|   BOOLEAN                 ShowDownArrow;
 | |
|   BOOLEAN                 ShowUpArrow;
 | |
|   UINTN                   DimensionsWidth;
 | |
|   LIST_ENTRY              *Link;
 | |
|   BOOLEAN                 OrderedList;
 | |
|   UINT8                   *ValueArray;
 | |
|   UINT8                   *ReturnValue;
 | |
|   UINT8                   ValueType;
 | |
|   EFI_HII_VALUE           HiiValue;
 | |
|   DISPLAY_QUESTION_OPTION         *OneOfOption;
 | |
|   DISPLAY_QUESTION_OPTION         *CurrentOption;
 | |
|   FORM_DISPLAY_ENGINE_STATEMENT  *Question;
 | |
|   INTN                    Result;
 | |
|   EFI_IFR_ORDERED_LIST    *OrderList;
 | |
| 
 | |
|   DimensionsWidth   = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
 | |
| 
 | |
|   ValueArray        = NULL;
 | |
|   ValueType         = 0;
 | |
|   CurrentOption     = NULL;
 | |
|   ShowDownArrow     = FALSE;
 | |
|   ShowUpArrow       = FALSE;
 | |
| 
 | |
|   StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
 | |
|   ASSERT (StringPtr);
 | |
| 
 | |
|   ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
 | |
| 
 | |
|   Question = MenuOption->ThisTag;
 | |
|   if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
 | |
|     Link = GetFirstNode (&Question->OptionListHead);
 | |
|     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|     ValueArray = Question->CurrentValue.Buffer;
 | |
|     ValueType =  OneOfOption->OptionOpCode->Type;
 | |
|     OrderedList = TRUE;
 | |
|     OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
 | |
|   } else {
 | |
|     OrderedList = FALSE;
 | |
|     OrderList = NULL;
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Calculate Option count
 | |
|   //
 | |
|   PopUpMenuLines = 0;
 | |
|   if (OrderedList) {
 | |
|     AdjustOptionOrder(Question, &PopUpMenuLines);
 | |
|   } else {
 | |
|     Link = GetFirstNode (&Question->OptionListHead);
 | |
|     while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|       PopUpMenuLines++;
 | |
|       Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Get the number of one of options present and its size
 | |
|   //
 | |
|   PopUpWidth = 0;
 | |
|   HighlightOptionIndex = 0;
 | |
|   Link = GetFirstNode (&Question->OptionListHead);
 | |
|   for (Index = 0; Index < PopUpMenuLines; Index++) {
 | |
|     OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
| 
 | |
|     StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|     if (StrLen (StringPtr) > PopUpWidth) {
 | |
|       PopUpWidth = StrLen (StringPtr);
 | |
|     }
 | |
|     FreePool (StringPtr);
 | |
|     HiiValue.Type = OneOfOption->OptionOpCode->Type;
 | |
|     SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
 | |
|     if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
 | |
|       //
 | |
|       // Find current selected Option for OneOf
 | |
|       //
 | |
|       HighlightOptionIndex = Index;
 | |
|     }
 | |
| 
 | |
|     Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|   }
 | |
| 
 | |
|   //
 | |
|   // Perform popup menu initialization.
 | |
|   //
 | |
|   PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
 | |
| 
 | |
|   SavedAttribute = gST->ConOut->Mode->Attribute;
 | |
|   gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
 | |
| 
 | |
|   if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
 | |
|     PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
 | |
|   }
 | |
| 
 | |
|   Start  = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
 | |
|   End    = Start + PopUpWidth + POPUP_FRAME_WIDTH;
 | |
|   Top    = gStatementDimensions.TopRow;
 | |
|   Bottom = gStatementDimensions.BottomRow - 1;
 | |
| 
 | |
|   MenuLinesInView = Bottom - Top - 1;
 | |
|   if (MenuLinesInView >= PopUpMenuLines) {
 | |
|     Top     = Top + (MenuLinesInView - PopUpMenuLines) / 2;
 | |
|     Bottom  = Top + PopUpMenuLines + 1;
 | |
|   } else {
 | |
|     ShowDownArrow = TRUE;
 | |
|   }
 | |
| 
 | |
|   if (HighlightOptionIndex > (MenuLinesInView - 1)) {
 | |
|     TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
 | |
|   } else {
 | |
|     TopOptionIndex = 0;
 | |
|   }
 | |
| 
 | |
|   do {
 | |
|     //
 | |
|     // Clear that portion of the screen
 | |
|     //
 | |
|     ClearLines (Start, End, Top, Bottom, GetPopupColor ());
 | |
| 
 | |
|     //
 | |
|     // Draw "One of" pop-up menu
 | |
|     //
 | |
|     Character = BOXDRAW_DOWN_RIGHT;
 | |
|     PrintCharAt (Start, Top, Character);
 | |
|     for (Index = Start; Index + 2 < End; Index++) {
 | |
|       if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
 | |
|         Character = GEOMETRICSHAPE_UP_TRIANGLE;
 | |
|       } else {
 | |
|         Character = BOXDRAW_HORIZONTAL;
 | |
|       }
 | |
| 
 | |
|       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|     }
 | |
| 
 | |
|     Character = BOXDRAW_DOWN_LEFT;
 | |
|     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|     Character = BOXDRAW_VERTICAL;
 | |
|     for (Index = Top + 1; Index < Bottom; Index++) {
 | |
|       PrintCharAt (Start, Index, Character);
 | |
|       PrintCharAt (End - 1, Index, Character);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Move to top Option
 | |
|     //
 | |
|     Link = GetFirstNode (&Question->OptionListHead);
 | |
|     for (Index = 0; Index < TopOptionIndex; Index++) {
 | |
|       Link = GetNextNode (&Question->OptionListHead, Link);
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // Display the One of options
 | |
|     //
 | |
|     Index2 = Top + 1;
 | |
|     for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
 | |
|       OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|       Link = GetNextNode (&Question->OptionListHead, Link);
 | |
| 
 | |
|       StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
 | |
|       ASSERT (StringPtr != NULL);
 | |
|       //
 | |
|       // If the string occupies multiple lines, truncate it to fit in one line,
 | |
|       // and append a "..." for indication.
 | |
|       //
 | |
|       if (StrLen (StringPtr) > (PopUpWidth - 1)) {
 | |
|         TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
 | |
|         ASSERT ( TempStringPtr != NULL );
 | |
|         CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
 | |
|         FreePool (StringPtr);
 | |
|         StringPtr = TempStringPtr;
 | |
|         StrCat (StringPtr, L"...");
 | |
|       }
 | |
| 
 | |
|       if (Index == HighlightOptionIndex) {
 | |
|           //
 | |
|           // Highlight the selected one
 | |
|           //
 | |
|           CurrentOption = OneOfOption;
 | |
| 
 | |
|           gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
 | |
|           PrintStringAt (Start + 2, Index2, StringPtr);
 | |
|           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
 | |
|         } else {
 | |
|           gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
 | |
|           PrintStringAt (Start + 2, Index2, StringPtr);
 | |
|         }
 | |
| 
 | |
|       Index2++;
 | |
|       FreePool (StringPtr);
 | |
|     }
 | |
| 
 | |
|     Character = BOXDRAW_UP_RIGHT;
 | |
|     PrintCharAt (Start, Bottom, Character);
 | |
|     for (Index = Start; Index + 2 < End; Index++) {
 | |
|       if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
 | |
|         Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
 | |
|       } else {
 | |
|         Character = BOXDRAW_HORIZONTAL;
 | |
|       }
 | |
| 
 | |
|       PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
|     }
 | |
| 
 | |
|     Character = BOXDRAW_UP_LEFT;
 | |
|     PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
 | |
| 
 | |
|     //
 | |
|     // Get User selection
 | |
|     //
 | |
|     Key.UnicodeChar = CHAR_NULL;
 | |
|     if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
 | |
|       Key.ScanCode  = gDirection;
 | |
|       gDirection    = 0;
 | |
|       goto TheKey;
 | |
|     }
 | |
| 
 | |
|     Status = WaitForKeyStroke (&Key);
 | |
| 
 | |
| TheKey:
 | |
|     switch (Key.UnicodeChar) {
 | |
|     case '+':
 | |
|       if (OrderedList) {
 | |
|         if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
 | |
|           //
 | |
|           // Highlight reaches the top of the popup window, scroll one menu item.
 | |
|           //
 | |
|           TopOptionIndex--;
 | |
|           ShowDownArrow = TRUE;
 | |
|         }
 | |
| 
 | |
|         if (TopOptionIndex == 0) {
 | |
|           ShowUpArrow = FALSE;
 | |
|         }
 | |
| 
 | |
|         if (HighlightOptionIndex > 0) {
 | |
|           HighlightOptionIndex--;
 | |
| 
 | |
|           ASSERT (CurrentOption != NULL);
 | |
|           SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case '-':
 | |
|       //
 | |
|       // If an ordered list op-code, we will allow for a popup of +/- keys
 | |
|       // to create an ordered list of items
 | |
|       //
 | |
|       if (OrderedList) {
 | |
|         if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
 | |
|             (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
 | |
|           //
 | |
|           // Highlight reaches the bottom of the popup window, scroll one menu item.
 | |
|           //
 | |
|           TopOptionIndex++;
 | |
|           ShowUpArrow = TRUE;
 | |
|         }
 | |
| 
 | |
|         if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
 | |
|           ShowDownArrow = FALSE;
 | |
|         }
 | |
| 
 | |
|         if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
 | |
|           HighlightOptionIndex++;
 | |
| 
 | |
|           ASSERT (CurrentOption != NULL);
 | |
|           SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
 | |
|         }
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     case CHAR_NULL:
 | |
|       switch (Key.ScanCode) {
 | |
|       case SCAN_UP:
 | |
|       case SCAN_DOWN:
 | |
|         if (Key.ScanCode == SCAN_UP) {
 | |
|           if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
 | |
|             //
 | |
|             // Highlight reaches the top of the popup window, scroll one menu item.
 | |
|             //
 | |
|             TopOptionIndex--;
 | |
|             ShowDownArrow = TRUE;
 | |
|           }
 | |
| 
 | |
|           if (TopOptionIndex == 0) {
 | |
|             ShowUpArrow = FALSE;
 | |
|           }
 | |
| 
 | |
|           if (HighlightOptionIndex > 0) {
 | |
|             HighlightOptionIndex--;
 | |
|           }
 | |
|         } else {
 | |
|           if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
 | |
|               (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
 | |
|             //
 | |
|             // Highlight reaches the bottom of the popup window, scroll one menu item.
 | |
|             //
 | |
|             TopOptionIndex++;
 | |
|             ShowUpArrow = TRUE;
 | |
|           }
 | |
| 
 | |
|           if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
 | |
|             ShowDownArrow = FALSE;
 | |
|           }
 | |
| 
 | |
|           if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
 | |
|             HighlightOptionIndex++;
 | |
|           }
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|       case SCAN_ESC:
 | |
|         gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
 | |
| 
 | |
|         //
 | |
|         // Restore link list order for orderedlist
 | |
|         //
 | |
|         if (OrderedList) {
 | |
|           HiiValue.Type = ValueType;
 | |
|           HiiValue.Value.u64 = 0;
 | |
|           for (Index = 0; Index < OrderList->MaxContainers; Index++) {
 | |
|             HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
 | |
|             if (HiiValue.Value.u64 == 0) {
 | |
|               break;
 | |
|             }
 | |
| 
 | |
|             OneOfOption = ValueToOption (Question, &HiiValue);
 | |
|             if (OneOfOption == NULL) {
 | |
|               return EFI_NOT_FOUND;
 | |
|             }
 | |
| 
 | |
|             RemoveEntryList (&OneOfOption->Link);
 | |
|             InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
 | |
|           }
 | |
|         }
 | |
| 
 | |
|         return EFI_DEVICE_ERROR;
 | |
| 
 | |
|       default:
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     case CHAR_CARRIAGE_RETURN:
 | |
|       //
 | |
|       // return the current selection
 | |
|       //
 | |
|       if (OrderedList) {
 | |
|         ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
 | |
|         ASSERT (ReturnValue != NULL);
 | |
|         Index = 0;
 | |
|         Link = GetFirstNode (&Question->OptionListHead);
 | |
|         while (!IsNull (&Question->OptionListHead, Link)) {
 | |
|           OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
 | |
|           Link = GetNextNode (&Question->OptionListHead, Link);
 | |
| 
 | |
|           SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
 | |
| 
 | |
|           Index++;
 | |
|           if (Index > OrderList->MaxContainers) {
 | |
|             break;
 | |
|           }
 | |
|         }
 | |
|         if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
 | |
|           FreePool (ReturnValue);
 | |
|           return EFI_DEVICE_ERROR;
 | |
|         } else {
 | |
|           gUserInput->InputValue.Buffer = ReturnValue;
 | |
|           gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
 | |
|           Status = EFI_SUCCESS;
 | |
|         }
 | |
|       } else {
 | |
|         ASSERT (CurrentOption != NULL);
 | |
|         gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
 | |
|         if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
 | |
|           return EFI_DEVICE_ERROR;
 | |
|         } else {
 | |
|           SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
 | |
|           Status = EFI_SUCCESS;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
 | |
| 
 | |
|       return EFI_SUCCESS;
 | |
|       
 | |
|     default:
 | |
|       break;
 | |
|     }
 | |
|   } while (TRUE);
 | |
| 
 | |
| }
 | |
| 
 |