/** Create popup window. It will replace CreateDialog(). This function draws OEM/Vendor specific pop up windows. @param[out] Key User Input Key @param ... String to be shown in Popup. The variable argument list is terminated by a NULL. **/ VOID EFIAPI CreateDialog ( OUT EFI_INPUT_KEY *Key, OPTIONAL ... ) { VA_LIST Marker; EFI_INPUT_KEY KeyValue; EFI_STATUS Status; UINTN LargestString; UINTN LineNum; UINTN Index; UINTN Count; CHAR16 Character; UINTN Start; UINTN End; UINTN Top; UINTN Bottom; CHAR16 *String; UINTN DimensionsWidth; UINTN DimensionsHeight; UINTN CurrentAttribute; BOOLEAN CursorVisible; // // If screen dimension info is not ready, get it from console. // if (gScreenDimensions.RightColumn == 0 || gScreenDimensions.BottomRow == 0) { ZeroMem (&gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR)); gST->ConOut->QueryMode ( gST->ConOut, gST->ConOut->Mode->Mode, &gScreenDimensions.RightColumn, &gScreenDimensions.BottomRow ); } DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn; DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow; LargestString = 0; LineNum = 0; VA_START (Marker, Key); while ((String = VA_ARG (Marker, CHAR16 *)) != NULL) { LineNum ++; if ((LibGetStringWidth (String) / 2) > LargestString) { LargestString = (LibGetStringWidth (String) / 2); } } VA_END (Marker); if ((LargestString + 2) > DimensionsWidth) { LargestString = DimensionsWidth - 2; } CurrentAttribute = gST->ConOut->Mode->Attribute; CursorVisible = gST->ConOut->Mode->CursorVisible; gST->ConOut->EnableCursor (gST->ConOut, FALSE); gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); // // Subtract the PopUp width from total Columns, allow for one space extra on // each end plus a border. // Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1; End = Start + LargestString + 1; Top = ((DimensionsHeight - LineNum - 2) / 2) + gScreenDimensions.TopRow - 1; Bottom = Top + LineNum + 2; Character = BOXDRAW_DOWN_RIGHT; PrintCharAt (Start, Top, Character); Character = BOXDRAW_HORIZONTAL; for (Index = Start; Index + 2 < End; Index++) { PrintCharAt ((UINTN)-1, (UINTN)-1, Character); } Character = BOXDRAW_DOWN_LEFT; PrintCharAt ((UINTN)-1, (UINTN)-1, Character); Character = BOXDRAW_VERTICAL; Count = 0; VA_START (Marker, Key); for (Index = Top; Index + 2 < Bottom; Index++, Count++) { String = VA_ARG (Marker, CHAR16*); if (String[0] == CHAR_NULL) { // // Passing in a NULL results in a blank space // ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); } else if (String[0] == L' ') { // // Passing in a space results in the assumption that this is where typing will occur // ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND); PrintStringAt ( ((DimensionsWidth - LibGetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1, Index + 1, String + 1 ); } else { // // This will clear the background of the line - we never know who might have been // here before us. This differs from the next clear in that it used the non-reverse // video for normal printing. // ClearLines (Start, End, Index + 1, Index + 1, GetPopupColor ()); PrintStringAt ( ((DimensionsWidth - LibGetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1, Index + 1, String ); } gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); PrintCharAt (Start, Index + 1, Character); PrintCharAt (End - 1, Index + 1, Character); } VA_END (Marker); Character = BOXDRAW_UP_RIGHT; PrintCharAt (Start, Bottom - 1, Character); Character = BOXDRAW_HORIZONTAL; for (Index = Start; Index + 2 < End; Index++) { PrintCharAt ((UINTN)-1, (UINTN) -1, Character); } Character = BOXDRAW_UP_LEFT; PrintCharAt ((UINTN)-1, (UINTN) -1, Character); if (Key != NULL) { Status = WaitForKeyStroke (&KeyValue); ASSERT_EFI_ERROR (Status); CopyMem (Key, &KeyValue, sizeof (EFI_INPUT_KEY)); } gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute); gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); }
/** This routine reads a numeric value from the user input. @param Selection Pointer to current selection. @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_SELECTION *Selection, 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_BROWSER_FORM *Form; FORM_BROWSER_FORMSET *FormSet; FORM_BROWSER_STATEMENT *Question; Column = MenuOption->OptCol; Row = MenuOption->Row; PreviousNumber[0] = 0; Count = 0; InputWidth = 0; Digital = 0; FormSet = Selection->FormSet; Form = Selection->Form; Question = MenuOption->ThisTag; QuestionValue = &Question->HiiValue; Step = Question->Step; Minimum = Question->Minimum; Maximum = Question->Maximum; // // 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->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) { DateOrTime = TRUE; } else { DateOrTime = FALSE; } // // Prepare Value to be edit // EraseLen = 0; EditValue = 0; if (Question->Operand == 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->Operand == 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 { // // Numeric // EraseLen = gOptionBlockWidth; EditValue = QuestionValue->Value.u64; if (Maximum == 0) { Maximum = (UINT64) -1; } } if ((Question->Operand == EFI_IFR_NUMERIC_OP) && ((Question->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->Operand == EFI_IFR_NUMERIC_OP) { if (HexInput) { InputWidth = Question->StorageWidth * 2; } else { switch (Question->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'; PrintAt (Column, Row, InputText); Column++; } if (Question->Operand == 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'; PrintAt (Column, Row, InputText); if (MenuOption->Sequence == 0) { Column++; } } if (Question->Operand == 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'; PrintAt (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->Operand == 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->Operand == 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, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND); for (Loop = 0; Loop < EraseLen; Loop++) { PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" "); } gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor)); if (MenuOption->Sequence == 0) { PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER); Column = MenuOption->OptCol + 1; } PrintStringAt (Column, Row, FormattedNumber); if (!DateOrTime || MenuOption->Sequence == 2) { PrintChar (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 (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE); break; } else { UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE); } // // Store Edit value back to Question // if (Question->Operand == 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->Operand == 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->Operand == EFI_IFR_DATE_OP && (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) { AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence); } // // Check to see if the Value is something reasonable against consistency limitations. // If not, let's kick the error specified. // Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); if (EFI_ERROR (Status)) { // // Input value is not valid, restore Question Value // GetQuestionValue (FormSet, Form, Question, TRUE); } else { SetQuestionValue (FormSet, Form, Question, TRUE); if (!DateOrTime || (Question->Storage != NULL)) { // // NV flag is unnecessary for RTC type of Date/Time // UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); } } return Status; break; case CHAR_BACKSPACE: if (ManualInput) { if (Count == 0) { break; } // // Remove a character // EditValue = PreviousNumber[Count - 1]; UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE); Count--; Column--; PrintAt (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 (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE); break; } } else { if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') { UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, 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 (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE); ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0])); EditValue = PreviousNumber[Count]; break; } else { UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE); } Count++; ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0]))); PreviousNumber[Count] = EditValue; PrintCharAt (Column, Row, Key.UnicodeChar); Column++; } break; } } while (TRUE); }
/** Get selection for OneOf and OrderedList (Left/Right will be ignored). @param Selection Pointer to current selection. @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_SELECTION *Selection, 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 ValueType; EFI_HII_VALUE HiiValue; EFI_HII_VALUE *HiiValueArray; UINTN OptionCount; QUESTION_OPTION *OneOfOption; QUESTION_OPTION *CurrentOption; FORM_BROWSER_STATEMENT *Question; INTN Result; DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn; ValueArray = NULL; ValueType = 0; CurrentOption = NULL; ShowDownArrow = FALSE; ShowUpArrow = FALSE; StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2); ASSERT (StringPtr); Question = MenuOption->ThisTag; if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) { ValueArray = Question->BufferValue; ValueType = Question->ValueType; OrderedList = TRUE; } else { OrderedList = FALSE; } // // Calculate Option count // if (OrderedList) { for (Index = 0; Index < Question->MaxContainers; Index++) { if (GetArrayData (ValueArray, ValueType, Index) == 0) { break; } } OptionCount = Index; } else { OptionCount = 0; Link = GetFirstNode (&Question->OptionListHead); while (!IsNull (&Question->OptionListHead, Link)) { OneOfOption = QUESTION_OPTION_FROM_LINK (Link); OptionCount++; Link = GetNextNode (&Question->OptionListHead, Link); } } // // Prepare HiiValue array // HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE)); ASSERT (HiiValueArray != NULL); Link = GetFirstNode (&Question->OptionListHead); for (Index = 0; Index < OptionCount; Index++) { if (OrderedList) { HiiValueArray[Index].Type = ValueType; HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index); } else { OneOfOption = QUESTION_OPTION_FROM_LINK (Link); CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE)); Link = GetNextNode (&Question->OptionListHead, Link); } } // // Move Suppressed Option to list tail // PopUpMenuLines = 0; for (Index = 0; Index < OptionCount; Index++) { OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]); if (OneOfOption == NULL) { return EFI_NOT_FOUND; } RemoveEntryList (&OneOfOption->Link); if ((OneOfOption->SuppressExpression != NULL) && EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) != ExpressFalse) { // // This option is suppressed, insert to tail // InsertTailList (&Question->OptionListHead, &OneOfOption->Link); } else { // // Insert to head // InsertHeadList (&Question->OptionListHead, &OneOfOption->Link); PopUpMenuLines++; } } // // 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 = QUESTION_OPTION_FROM_LINK (Link); StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle); if (StrLen (StringPtr) > PopUpWidth) { PopUpWidth = StrLen (StringPtr); } FreePool (StringPtr); if (!OrderedList && (CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, &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, POPUP_TEXT | POPUP_BACKGROUND); if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) { PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH; } Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn; End = Start + PopUpWidth + POPUP_FRAME_WIDTH; Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT; Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - 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, POPUP_TEXT | POPUP_BACKGROUND); // // 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; } PrintChar (Character); } Character = BOXDRAW_DOWN_LEFT; PrintChar (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 = QUESTION_OPTION_FROM_LINK (Link); Link = GetNextNode (&Question->OptionListHead, Link); StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle); 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, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND); PrintStringAt (Start + 2, Index2, StringPtr); gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND); } else { gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND); 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; } PrintChar (Character); } Character = BOXDRAW_UP_LEFT; PrintChar (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 < Question->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); } } FreePool (HiiValueArray); return EFI_DEVICE_ERROR; default: break; } break; case CHAR_CARRIAGE_RETURN: // // return the current selection // if (OrderedList) { Index = 0; Link = GetFirstNode (&Question->OptionListHead); while (!IsNull (&Question->OptionListHead, Link)) { OneOfOption = QUESTION_OPTION_FROM_LINK (Link); SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64); Index++; if (Index > Question->MaxContainers) { break; } Link = GetNextNode (&Question->OptionListHead, Link); } } else { ASSERT (CurrentOption != NULL); CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE)); } gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); FreePool (HiiValueArray); Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); if (EFI_ERROR (Status)) { // // Input value is not valid, restore Question Value // GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); } else { SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); } return Status; default: break; } } while (TRUE); }
/** Print framework and form title for a page. @param[in] FormData Form Data to be shown in Page **/ VOID PrintFramework ( IN FORM_DISPLAY_ENGINE_FORM *FormData ) { UINTN Index; CHAR16 Character; CHAR16 *Buffer; UINTN Row; CHAR16 *TitleStr; UINTN TitleColumn; if (gClassOfVfr != FORMSET_CLASS_PLATFORM_SETUP) { // // Only Setup page needs Framework // ClearLines ( gScreenDimensions.LeftColumn, gScreenDimensions.RightColumn, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 1, KEYHELP_TEXT | KEYHELP_BACKGROUND ); return; } Buffer = AllocateZeroPool (0x10000); ASSERT (Buffer != NULL); Character = BOXDRAW_HORIZONTAL; for (Index = 0; Index + 2 < (gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn); Index++) { Buffer[Index] = Character; } // // Print Top border line // +------------------------------------------------------------------------------+ // ? ? // +------------------------------------------------------------------------------+ // gST->ConOut->SetAttribute (gST->ConOut, TITLE_TEXT | TITLE_BACKGROUND); Character = BOXDRAW_DOWN_RIGHT; PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.TopRow, Character); PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); Character = BOXDRAW_DOWN_LEFT; PrintCharAt ((UINTN) -1, (UINTN) -1, Character); Character = BOXDRAW_VERTICAL; for (Row = gScreenDimensions.TopRow + 1; Row <= gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 2; Row++) { PrintCharAt (gScreenDimensions.LeftColumn, Row, Character); PrintCharAt (gScreenDimensions.RightColumn - 1, Row, Character); } // // Print Form Title // TitleStr = LibGetToken (FormData->FormTitle, FormData->HiiHandle); ASSERT (TitleStr != NULL); TitleColumn = (gScreenDimensions.RightColumn + gScreenDimensions.LeftColumn - LibGetStringWidth (TitleStr) / 2) / 2; PrintStringAtWithWidth (gScreenDimensions.LeftColumn + 1, gScreenDimensions.TopRow + 1, gLibEmptyString, TitleColumn - gScreenDimensions.LeftColumn - 1); PrintStringAtWithWidth ( TitleColumn, gScreenDimensions.TopRow + 1, TitleStr, gScreenDimensions.RightColumn - 1 - TitleColumn ); FreePool (TitleStr); Character = BOXDRAW_UP_RIGHT; PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT - 1, Character); PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); Character = BOXDRAW_UP_LEFT; PrintCharAt ((UINTN) -1, (UINTN) -1, Character); // // Print Bottom border line // +------------------------------------------------------------------------------+ // ? ? // +------------------------------------------------------------------------------+ // Character = BOXDRAW_DOWN_RIGHT; PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight, Character); PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); Character = BOXDRAW_DOWN_LEFT; PrintCharAt ((UINTN) -1, (UINTN) -1, Character); Character = BOXDRAW_VERTICAL; for (Row = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight + 1; Row <= gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 2; Row++ ) { PrintCharAt (gScreenDimensions.LeftColumn, Row, Character); PrintCharAt (gScreenDimensions.RightColumn - 1, Row, Character); } Character = BOXDRAW_UP_RIGHT; PrintCharAt (gScreenDimensions.LeftColumn, gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - 1, Character); PrintStringAt ((UINTN) -1, (UINTN) -1, Buffer); Character = BOXDRAW_UP_LEFT; PrintCharAt ((UINTN) -1, (UINTN) -1, Character); FreePool (Buffer); }
/** 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); }
/** 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); }
EFI_STATUS GetNumericInput ( IN UI_MENU_SELECTION *Selection, IN UI_MENU_OPTION *MenuOption ) /*++ Routine Description: This routine reads a numeric value from the user input. Arguments: Selection - Pointer to current selection. MenuOption - Pointer to the current input menu. Returns: EFI_SUCCESS - If numerical input is read successfully EFI_DEVICE_ERROR - If operation fails --*/ { EFI_STATUS Status; UINTN Column; UINTN Row; CHAR16 InputText[23]; CHAR16 FormattedNumber[22]; UINT64 PreviousNumber[20]; 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_BROWSER_FORM *Form; FORM_BROWSER_FORMSET *FormSet; FORM_BROWSER_STATEMENT *Question; Column = MenuOption->OptCol; Row = MenuOption->Row; PreviousNumber[0] = 0; Count = 0; InputWidth = 0; Digital = 0; FormSet = Selection->FormSet; Form = Selection->Form; Question = MenuOption->ThisTag; QuestionValue = &Question->HiiValue; Step = Question->Step; Minimum = Question->Minimum; Maximum = Question->Maximum; if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) { DateOrTime = TRUE; } else { DateOrTime = FALSE; } // // Prepare Value to be edit // EraseLen = 0; EditValue = 0; if (Question->Operand == 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: Maximum = 31; 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->Operand == 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 { // // Numeric // EraseLen = gOptionBlockWidth; EditValue = QuestionValue->Value.u64; if (Maximum == 0) { Maximum = (UINT64) -1; } } if (Step == 0) { ManualInput = TRUE; } else { ManualInput = FALSE; } if ((Question->Operand == EFI_IFR_NUMERIC_OP) && ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) { HexInput = TRUE; } else { HexInput = FALSE; } if (ManualInput) { if (HexInput) { InputWidth = Question->StorageWidth * 2; } else { switch (Question->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' '); InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; InputText[InputWidth + 2] = L'\0'; PrintAt (Column, Row, InputText); 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) { // // 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 (!ManualInput) { if (Key.ScanCode == SCAN_LEFT) { if (EditValue > Step) { EditValue = EditValue - Step; } else { EditValue = Minimum; } } else if (Key.ScanCode == SCAN_RIGHT) { EditValue = EditValue + Step; if (EditValue > Maximum) { EditValue = Maximum; } } EfiZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); if (Question->Operand == EFI_IFR_DATE_OP) { if (MenuOption->Sequence == 2) { // // Year // SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINTN) EditValue); } else { // // Month/Day // SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINTN) EditValue); } if (MenuOption->Sequence == 0) { FormattedNumber[EraseLen - 2] = DATE_SEPARATOR; } else if (MenuOption->Sequence == 1) { FormattedNumber[EraseLen - 1] = DATE_SEPARATOR; } } else if (Question->Operand == EFI_IFR_TIME_OP) { SPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINTN) EditValue); if (MenuOption->Sequence == 0) { FormattedNumber[EraseLen - 2] = TIME_SEPARATOR; } else if (MenuOption->Sequence == 1) { FormattedNumber[EraseLen - 1] = TIME_SEPARATOR; } } else { QuestionValue->Value.u64 = EditValue; PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); } gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND); for (Loop = 0; Loop < EraseLen; Loop++) { PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" "); } gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT); if (MenuOption->Sequence == 0) { PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER); Column = MenuOption->OptCol + 1; } PrintStringAt (Column, Row, FormattedNumber); if (!DateOrTime || MenuOption->Sequence == 2) { PrintChar (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: // // Store Edit value back to Question // if (Question->Operand == 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->Operand == 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; } // // Check to see if the Value is something reasonable against consistency limitations. // If not, let's kick the error specified. // Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); if (EFI_ERROR (Status)) { // // Input value is not valid, restore Question Value // GetQuestionValue (FormSet, Form, Question, TRUE); } else { SetQuestionValue (FormSet, Form, Question, TRUE); if (!DateOrTime || (Question->Storage != NULL)) { // // NV flag is unnecessary for RTC type of Date/Time // UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); } } return Status; break; case CHAR_BACKSPACE: if (ManualInput) { if (Count == 0) { break; } // // Remove a character // EditValue = PreviousNumber[Count - 1]; UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE); Count--; Column--; PrintAt (Column, Row, L" "); } break; default: if (ManualInput) { if (HexInput) { if (!IsHexDigit (&Digital, Key.UnicodeChar)) { UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE); break; } } else { if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') { UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, 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 = EditValue * 10 + (Key.UnicodeChar - L'0'); // EditValue = LShiftU64 (EditValue, 3) + LShiftU64 (EditValue, 1) + (Key.UnicodeChar - L'0'); } } else { if (HexInput) { EditValue = Digital; } else { EditValue = Key.UnicodeChar - L'0'; } } if (EditValue > Maximum) { UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE); EditValue = PreviousNumber[Count]; break; } else { UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE); } Count++; PreviousNumber[Count] = EditValue; PrintCharAt (Column, Row, Key.UnicodeChar); Column++; } break; } } while (TRUE); return EFI_SUCCESS; }
/** 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 ) { 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 IntInput; BOOLEAN Negative; BOOLEAN ValidateFail; 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; IntInput = FALSE; HexInput = FALSE; Negative = FALSE; ValidateFail = FALSE; Question = MenuOption->ThisTag; QuestionValue = &Question->CurrentValue; ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16)); // // 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, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth); EraseLen = gOptionBlockWidth; } if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) { if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){ HexInput = TRUE; } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){ // // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number. // IntInput = TRUE; } } // // 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; } if (IntInput) { // // Support an extra '-' for negative number. // InputWidth += 1; } } 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' '); InputText[InputWidth + 1] = DATE_SEPARATOR; InputText[InputWidth + 2] = L'\0'; } else if (MenuOption->Sequence == 1){ SetUnicodeMem (InputText, InputWidth, L' '); InputText[InputWidth] = DATE_SEPARATOR; InputText[InputWidth + 1] = L'\0'; } else { SetUnicodeMem (InputText, InputWidth, L' '); InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER; InputText[InputWidth + 1] = 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' '); InputText[InputWidth + 1] = TIME_SEPARATOR; InputText[InputWidth + 2] = L'\0'; } else if (MenuOption->Sequence == 1){ SetUnicodeMem (InputText, InputWidth, L' '); InputText[InputWidth] = TIME_SEPARATOR; InputText[InputWidth + 1] = L'\0'; } else { SetUnicodeMem (InputText, InputWidth, L' '); InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER; InputText[InputWidth + 1] = 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; } WaitForKeyStroke (&Key); TheKey2: switch (Key.UnicodeChar) { case '+': case '-': if (ManualInput && IntInput) { // // In Manual input mode, check whether input the negative flag. // if (Key.UnicodeChar == '-') { if (Negative) { break; } Negative = TRUE; PrintCharAt (Column++, Row, Key.UnicodeChar); } } else { if (Key.UnicodeChar == '+') { Key.ScanCode = SCAN_RIGHT; } else { Key.ScanCode = SCAN_LEFT; } Key.UnicodeChar = CHAR_NULL; goto TheKey2; } break; 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 (IntInput) { if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) { EditValue = EditValue - Step; } else if ((INT64) EditValue > (INT64) Minimum){ EditValue = Minimum; } else { EditValue = Maximum; } } else { if (EditValue >= Minimum + Step) { EditValue = EditValue - Step; } else if (EditValue > Minimum){ EditValue = Minimum; } else { EditValue = Maximum; } } } else if (Key.ScanCode == SCAN_RIGHT) { if (IntInput) { if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) { EditValue = EditValue + Step; } else if ((INT64) EditValue < (INT64) Maximum) { EditValue = Maximum; } else { EditValue = Minimum; } } else { 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; 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. // ValidateFail = FALSE; if (IntInput) { // // After user input Enter, need to check whether the input value. // If input a negative value, should compare with maximum value. // else compare with the minimum value. // if (Negative) { ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; } else { ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; } if (ValidateFail) { UpdateStatusBar (INPUT_ERROR, TRUE); break; } } else if (EditValue < Minimum) { UpdateStatusBar (INPUT_ERROR, TRUE); break; } 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; case CHAR_BACKSPACE: if (ManualInput) { if (Count == 0) { if (Negative) { Negative = FALSE; Column--; PrintStringAt (Column, Row, L" "); } 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 if (IntInput && Negative) { // // Save the negative number. // EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1; } else { EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0'); } } else { if (HexInput) { EditValue = Digital; } else if (IntInput && Negative) { // // Save the negative number. // EditValue = ~(Key.UnicodeChar - L'0') + 1; } else { EditValue = Key.UnicodeChar - L'0'; } } if (IntInput) { ValidateFail = FALSE; // // When user input a new value, should check the current value. // If user input a negative value, should compare it with minimum // value, else compare it with maximum value. // if (Negative) { ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; } else { ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; } if (ValidateFail) { UpdateStatusBar (INPUT_ERROR, TRUE); ASSERT (Count < ARRAY_SIZE (PreviousNumber)); EditValue = PreviousNumber[Count]; break; } } else { if (EditValue > Maximum) { UpdateStatusBar (INPUT_ERROR, TRUE); ASSERT (Count < ARRAY_SIZE (PreviousNumber)); EditValue = PreviousNumber[Count]; break; } } UpdateStatusBar (INPUT_ERROR, FALSE); Count++; ASSERT (Count < (ARRAY_SIZE (PreviousNumber))); PreviousNumber[Count] = EditValue; gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); PrintCharAt (Column, Row, Key.UnicodeChar); Column++; } break; } } while (TRUE); }