/** 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; }
/** Process a Question's Option (whether selected or un-selected). @param Selection Pointer to UI_MENU_SELECTION. @param MenuOption The MenuOption for this Question. @param Selected TRUE: if Question is selected. @param OptionString Pointer of the Option String to be displayed. @retval EFI_SUCCESS Question Option process success. @retval Other Question Option process fail. **/ EFI_STATUS ProcessOptions ( IN UI_MENU_SELECTION *Selection, IN UI_MENU_OPTION *MenuOption, IN BOOLEAN Selected, OUT CHAR16 **OptionString ) { EFI_STATUS Status; CHAR16 *StringPtr; CHAR16 *TempString; UINTN Index; FORM_BROWSER_STATEMENT *Question; CHAR16 FormattedNumber[21]; UINT16 Number; CHAR16 Character[2]; EFI_INPUT_KEY Key; UINTN BufferSize; QUESTION_OPTION *OneOfOption; LIST_ENTRY *Link; EFI_HII_VALUE HiiValue; EFI_HII_VALUE *QuestionValue; BOOLEAN Suppress; UINT16 Maximum; QUESTION_OPTION *Option; UINTN Index2; UINT8 *ValueArray; UINT8 ValueType; EFI_STRING_ID StringId; Status = EFI_SUCCESS; StringPtr = NULL; Character[1] = L'\0'; *OptionString = NULL; StringId = 0; ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); BufferSize = (gOptionBlockWidth + 1) * 2 * gScreenDimensions.BottomRow; Question = MenuOption->ThisTag; QuestionValue = &Question->HiiValue; Maximum = (UINT16) Question->Maximum; ValueArray = Question->BufferValue; ValueType = Question->ValueType; switch (Question->Operand) { case EFI_IFR_ORDERED_LIST_OP: // // Check whether there are Options of this OrderedList // if (IsListEmpty (&Question->OptionListHead)) { break; } // // Initialize Option value array // if (GetArrayData (ValueArray, ValueType, 0) == 0) { GetQuestionDefault (Selection->FormSet, Selection->Form, Question, 0); } if (Selected) { // // Go ask for input // Status = GetSelectionInputPopUp (Selection, MenuOption); } else { // // We now know how many strings we will have, so we can allocate the // space required for the array or strings. // *OptionString = AllocateZeroPool (Question->MaxContainers * BufferSize); ASSERT (*OptionString); 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) { // // Values for the options in ordered lists should never be a 0 // break; } OneOfOption = ValueToOption (Question, &HiiValue); if (OneOfOption == NULL) { // // Show error message // do { CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); // // The initial value of the orderedlist is invalid, force to be valid value // Link = GetFirstNode (&Question->OptionListHead); Index2 = 0; while (!IsNull (&Question->OptionListHead, Link) && Index2 < Question->MaxContainers) { Option = QUESTION_OPTION_FROM_LINK (Link); SetArrayData (ValueArray, ValueType, Index2, Option->Value.Value.u64); Index2++; Link = GetNextNode (&Question->OptionListHead, Link); } SetArrayData (ValueArray, ValueType, Index2, 0); Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); FreePool (*OptionString); *OptionString = NULL; return EFI_NOT_FOUND; } Suppress = FALSE; if ((OneOfOption->SuppressExpression != NULL) && (EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressSuppress)) { // // This option is suppressed // Suppress = TRUE; } if (!Suppress) { Character[0] = LEFT_ONEOF_DELIMITER; NewStrCat (OptionString[0], Character); StringPtr = GetToken (OneOfOption->Text, Selection->Handle); ASSERT (StringPtr != NULL); NewStrCat (OptionString[0], StringPtr); Character[0] = RIGHT_ONEOF_DELIMITER; NewStrCat (OptionString[0], Character); Character[0] = CHAR_CARRIAGE_RETURN; NewStrCat (OptionString[0], Character); FreePool (StringPtr); } } } break; case EFI_IFR_ONE_OF_OP: // // Check whether there are Options of this OneOf // if (IsListEmpty (&Question->OptionListHead)) { break; } if (Selected) { // // Go ask for input // Status = GetSelectionInputPopUp (Selection, MenuOption); } else { *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); OneOfOption = ValueToOption (Question, QuestionValue); if (OneOfOption == NULL) { // // Show error message // do { CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gOptionMismatch, gPressEnter, gEmptyString); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); // // Force the Question value to be valid // Link = GetFirstNode (&Question->OptionListHead); while (!IsNull (&Question->OptionListHead, Link)) { Option = QUESTION_OPTION_FROM_LINK (Link); if ((Option->SuppressExpression == NULL) || (EvaluateExpressionList(Option->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse)) { CopyMem (QuestionValue, &Option->Value, sizeof (EFI_HII_VALUE)); SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); break; } Link = GetNextNode (&Question->OptionListHead, Link); } FreePool (*OptionString); *OptionString = NULL; return EFI_NOT_FOUND; } if ((OneOfOption->SuppressExpression != NULL) && ((EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressSuppress))) { // // This option is suppressed // Suppress = TRUE; } else { Suppress = FALSE; } if (Suppress) { // // Current selected option happen to be suppressed, // enforce to select on a non-suppressed option // Link = GetFirstNode (&Question->OptionListHead); while (!IsNull (&Question->OptionListHead, Link)) { OneOfOption = QUESTION_OPTION_FROM_LINK (Link); if ((OneOfOption->SuppressExpression == NULL) || (EvaluateExpressionList(OneOfOption->SuppressExpression, FALSE, NULL, NULL) == ExpressFalse)) { Suppress = FALSE; CopyMem (QuestionValue, &OneOfOption->Value, sizeof (EFI_HII_VALUE)); SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND); break; } Link = GetNextNode (&Question->OptionListHead, Link); } } if (!Suppress) { Character[0] = LEFT_ONEOF_DELIMITER; NewStrCat (OptionString[0], Character); StringPtr = GetToken (OneOfOption->Text, Selection->Handle); ASSERT (StringPtr != NULL); NewStrCat (OptionString[0], StringPtr); Character[0] = RIGHT_ONEOF_DELIMITER; NewStrCat (OptionString[0], Character); FreePool (StringPtr); } } break; case EFI_IFR_CHECKBOX_OP: *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); *OptionString[0] = LEFT_CHECKBOX_DELIMITER; if (Selected) { // // Since this is a BOOLEAN operation, flip it upon selection // QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE); // // Perform inconsistent check // Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); if (EFI_ERROR (Status)) { // // Inconsistent check fail, restore Question Value // QuestionValue->Value.b = (BOOLEAN) (QuestionValue->Value.b ? FALSE : TRUE); FreePool (*OptionString); *OptionString = NULL; return Status; } // // Save Question value // Status = SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); } if (QuestionValue->Value.b) { *(OptionString[0] + 1) = CHECK_ON; } else { *(OptionString[0] + 1) = CHECK_OFF; } *(OptionString[0] + 2) = RIGHT_CHECKBOX_DELIMITER; break; case EFI_IFR_NUMERIC_OP: if (Selected) { // // Go ask for input // Status = GetNumericInput (Selection, MenuOption); } else { *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); *OptionString[0] = LEFT_NUMERIC_DELIMITER; // // Formatted print // PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); Number = (UINT16) GetStringWidth (FormattedNumber); CopyMem (OptionString[0] + 1, FormattedNumber, Number); *(OptionString[0] + Number / 2) = RIGHT_NUMERIC_DELIMITER; } break; case EFI_IFR_DATE_OP: if (Selected) { // // This is similar to numerics // Status = GetNumericInput (Selection, MenuOption); } else { *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); switch (MenuOption->Sequence) { case 0: *OptionString[0] = LEFT_NUMERIC_DELIMITER; UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Month); *(OptionString[0] + 3) = DATE_SEPARATOR; break; case 1: SetUnicodeMem (OptionString[0], 4, L' '); UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.date.Day); *(OptionString[0] + 6) = DATE_SEPARATOR; break; case 2: SetUnicodeMem (OptionString[0], 7, L' '); UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%04d", QuestionValue->Value.date.Year); *(OptionString[0] + 11) = RIGHT_NUMERIC_DELIMITER; break; } } break; case EFI_IFR_TIME_OP: if (Selected) { // // This is similar to numerics // Status = GetNumericInput (Selection, MenuOption); } else { *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); switch (MenuOption->Sequence) { case 0: *OptionString[0] = LEFT_NUMERIC_DELIMITER; UnicodeSPrint (OptionString[0] + 1, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Hour); *(OptionString[0] + 3) = TIME_SEPARATOR; break; case 1: SetUnicodeMem (OptionString[0], 4, L' '); UnicodeSPrint (OptionString[0] + 4, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Minute); *(OptionString[0] + 6) = TIME_SEPARATOR; break; case 2: SetUnicodeMem (OptionString[0], 7, L' '); UnicodeSPrint (OptionString[0] + 7, 21 * sizeof (CHAR16), L"%02d", QuestionValue->Value.time.Second); *(OptionString[0] + 9) = RIGHT_NUMERIC_DELIMITER; break; } } break; case EFI_IFR_STRING_OP: if (Selected) { StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); ASSERT (StringPtr); CopyMem(StringPtr, Question->BufferValue, Maximum * sizeof (CHAR16)); Status = ReadString (MenuOption, gPromptForData, StringPtr); if (!EFI_ERROR (Status)) { HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, StringPtr, NULL); Status = ValidateQuestion(Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); if (EFI_ERROR (Status)) { HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL); } else { CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16)); SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE); UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE); } } FreePool (StringPtr); } else { *OptionString = AllocateZeroPool (BufferSize); ASSERT (*OptionString); if (((CHAR16 *) Question->BufferValue)[0] == 0x0000) { *(OptionString[0]) = '_'; } else { if ((Maximum * sizeof (CHAR16)) < BufferSize) { BufferSize = Maximum * sizeof (CHAR16); } CopyMem (OptionString[0], (CHAR16 *) Question->BufferValue, BufferSize); } } break; case EFI_IFR_PASSWORD_OP: if (Selected) { StringPtr = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); ASSERT (StringPtr); // // For interactive passwords, old password is validated by callback // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { // // Use a NULL password to test whether old password is required // *StringPtr = 0; Status = PasswordCallback (Selection, MenuOption, StringPtr); if (Status == EFI_NOT_AVAILABLE_YET || Status == EFI_UNSUPPORTED) { // // Callback is not supported, or // Callback request to terminate password input // FreePool (StringPtr); return EFI_SUCCESS; } if (EFI_ERROR (Status)) { // // Old password exist, ask user for the old password // Status = ReadString (MenuOption, gPromptForPassword, StringPtr); if (EFI_ERROR (Status)) { FreePool (StringPtr); return Status; } // // Check user input old password // Status = PasswordCallback (Selection, MenuOption, StringPtr); if (EFI_ERROR (Status)) { if (Status == EFI_NOT_READY) { // // Typed in old password incorrect // PasswordInvalid (); } else { Status = EFI_SUCCESS; } FreePool (StringPtr); return Status; } } } else { // // For non-interactive password, validate old password in local // if (*((CHAR16 *) Question->BufferValue) != 0) { // // There is something there! Prompt for password // Status = ReadString (MenuOption, gPromptForPassword, StringPtr); if (EFI_ERROR (Status)) { FreePool (StringPtr); return Status; } TempString = AllocateCopyPool ((Maximum + 1) * sizeof (CHAR16), Question->BufferValue); ASSERT (TempString != NULL); TempString[Maximum] = L'\0'; if (StrCmp (StringPtr, TempString) != 0) { // // Typed in old password incorrect // PasswordInvalid (); FreePool (StringPtr); FreePool (TempString); return Status; } FreePool (TempString); } } // // Ask for new password // ZeroMem (StringPtr, (Maximum + 1) * sizeof (CHAR16)); Status = ReadString (MenuOption, gPromptForNewPassword, StringPtr); if (EFI_ERROR (Status)) { // // Reset state machine for interactive password // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { PasswordCallback (Selection, MenuOption, NULL); } FreePool (StringPtr); return Status; } // // Confirm new password // TempString = AllocateZeroPool ((Maximum + 1) * sizeof (CHAR16)); ASSERT (TempString); Status = ReadString (MenuOption, gConfirmPassword, TempString); if (EFI_ERROR (Status)) { // // Reset state machine for interactive password // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { PasswordCallback (Selection, MenuOption, NULL); } FreePool (StringPtr); FreePool (TempString); return Status; } // // Compare two typed-in new passwords // if (StrCmp (StringPtr, TempString) == 0) { // // Prepare the Question->HiiValue.Value.string for ValidateQuestion use. // if((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { StringId = Question->HiiValue.Value.string; Question->HiiValue.Value.string = NewString (StringPtr, Selection->FormSet->HiiHandle); } else { HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, StringPtr, NULL); } Status = ValidateQuestion(Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF); // // Researve the Question->HiiValue.Value.string. // if((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { DeleteString(Question->HiiValue.Value.string, Selection->FormSet->HiiHandle); Question->HiiValue.Value.string = StringId; } if (EFI_ERROR (Status)) { // // Reset state machine for interactive password // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { PasswordCallback (Selection, MenuOption, NULL); } else { // // Researve the Question->HiiValue.Value.string. // HiiSetString(Selection->FormSet->HiiHandle, Question->HiiValue.Value.string, (CHAR16*)Question->BufferValue, NULL); } } else { // // Two password match, send it to Configuration Driver // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { PasswordCallback (Selection, MenuOption, StringPtr); } else { CopyMem (Question->BufferValue, StringPtr, Maximum * sizeof (CHAR16)); SetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE); } } } else { // // Reset state machine for interactive password // if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) { PasswordCallback (Selection, MenuOption, NULL); } // // Two password mismatch, prompt error message // do { CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gConfirmError, gPressEnter, gEmptyString); } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); } FreePool (TempString); FreePool (StringPtr); } break; default: break; } return Status; }
/** 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); }
/** 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); }