1 /** @file 2 Implementation for handling user input from the User Interfaces. 3 4 Copyright (c) 2004 - 2016, Intel Corporation. All rights reserved.<BR> 5 This program and the accompanying materials 6 are licensed and made available under the terms and conditions of the BSD License 7 which accompanies this distribution. The full text of the license may be found at 8 http://opensource.org/licenses/bsd-license.php 9 10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 12 13 **/ 14 15 #include "FormDisplay.h" 16 17 /** 18 Get maximum and minimum info from this opcode. 19 20 @param OpCode Pointer to the current input opcode. 21 @param Minimum The minimum size info for this opcode. 22 @param Maximum The maximum size info for this opcode. 23 24 **/ 25 VOID 26 GetFieldFromOp ( 27 IN EFI_IFR_OP_HEADER *OpCode, 28 OUT UINTN *Minimum, 29 OUT UINTN *Maximum 30 ) 31 { 32 EFI_IFR_STRING *StringOp; 33 EFI_IFR_PASSWORD *PasswordOp; 34 if (OpCode->OpCode == EFI_IFR_STRING_OP) { 35 StringOp = (EFI_IFR_STRING *) OpCode; 36 *Minimum = StringOp->MinSize; 37 *Maximum = StringOp->MaxSize; 38 } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) { 39 PasswordOp = (EFI_IFR_PASSWORD *) OpCode; 40 *Minimum = PasswordOp->MinSize; 41 *Maximum = PasswordOp->MaxSize; 42 } else { 43 *Minimum = 0; 44 *Maximum = 0; 45 } 46 } 47 48 /** 49 Get string or password input from user. 50 51 @param MenuOption Pointer to the current input menu. 52 @param Prompt The prompt string shown on popup window. 53 @param StringPtr Old user input and destination for use input string. 54 55 @retval EFI_SUCCESS If string input is read successfully 56 @retval EFI_DEVICE_ERROR If operation fails 57 58 **/ 59 EFI_STATUS 60 ReadString ( 61 IN UI_MENU_OPTION *MenuOption, 62 IN CHAR16 *Prompt, 63 IN OUT CHAR16 *StringPtr 64 ) 65 { 66 EFI_STATUS Status; 67 EFI_INPUT_KEY Key; 68 CHAR16 NullCharacter; 69 UINTN ScreenSize; 70 CHAR16 Space[2]; 71 CHAR16 KeyPad[2]; 72 CHAR16 *TempString; 73 CHAR16 *BufferedString; 74 UINTN Index; 75 UINTN Index2; 76 UINTN Count; 77 UINTN Start; 78 UINTN Top; 79 UINTN DimensionsWidth; 80 UINTN DimensionsHeight; 81 UINTN CurrentCursor; 82 BOOLEAN CursorVisible; 83 UINTN Minimum; 84 UINTN Maximum; 85 FORM_DISPLAY_ENGINE_STATEMENT *Question; 86 BOOLEAN IsPassword; 87 UINTN MaxLen; 88 89 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; 90 DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow; 91 92 NullCharacter = CHAR_NULL; 93 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16); 94 Space[0] = L' '; 95 Space[1] = CHAR_NULL; 96 97 Question = MenuOption->ThisTag; 98 GetFieldFromOp(Question->OpCode, &Minimum, &Maximum); 99 100 if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) { 101 IsPassword = TRUE; 102 } else { 103 IsPassword = FALSE; 104 } 105 106 MaxLen = Maximum + 1; 107 TempString = AllocateZeroPool (MaxLen * sizeof (CHAR16)); 108 ASSERT (TempString); 109 110 if (ScreenSize < (Maximum + 1)) { 111 ScreenSize = Maximum + 1; 112 } 113 114 if ((ScreenSize + 2) > DimensionsWidth) { 115 ScreenSize = DimensionsWidth - 2; 116 } 117 118 BufferedString = AllocateZeroPool (ScreenSize * 2); 119 ASSERT (BufferedString); 120 121 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1; 122 Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1; 123 124 // 125 // Display prompt for string 126 // 127 // CreateDialog (NULL, "", Prompt, Space, "", NULL); 128 CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter); 129 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); 130 131 CursorVisible = gST->ConOut->Mode->CursorVisible; 132 gST->ConOut->EnableCursor (gST->ConOut, TRUE); 133 134 CurrentCursor = GetStringWidth (StringPtr) / 2 - 1; 135 if (CurrentCursor != 0) { 136 // 137 // Show the string which has beed saved before. 138 // 139 SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); 140 PrintStringAt (Start + 1, Top + 3, BufferedString); 141 142 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { 143 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; 144 } else { 145 Index = 0; 146 } 147 148 if (IsPassword) { 149 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); 150 } 151 152 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { 153 BufferedString[Count] = StringPtr[Index]; 154 155 if (IsPassword) { 156 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); 157 } 158 } 159 160 if (!IsPassword) { 161 PrintStringAt (Start + 1, Top + 3, BufferedString); 162 } 163 164 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); 165 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3); 166 } 167 168 do { 169 Status = WaitForKeyStroke (&Key); 170 ASSERT_EFI_ERROR (Status); 171 172 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY)); 173 switch (Key.UnicodeChar) { 174 case CHAR_NULL: 175 switch (Key.ScanCode) { 176 case SCAN_LEFT: 177 if (CurrentCursor > 0) { 178 CurrentCursor--; 179 } 180 break; 181 182 case SCAN_RIGHT: 183 if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) { 184 CurrentCursor++; 185 } 186 break; 187 188 case SCAN_ESC: 189 FreePool (TempString); 190 FreePool (BufferedString); 191 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); 192 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); 193 return EFI_DEVICE_ERROR; 194 195 case SCAN_DELETE: 196 for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) { 197 StringPtr[Index] = StringPtr[Index + 1]; 198 PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL? L'*' : StringPtr[Index]); 199 } 200 break; 201 202 default: 203 break; 204 } 205 206 break; 207 208 case CHAR_CARRIAGE_RETURN: 209 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) { 210 211 FreePool (TempString); 212 FreePool (BufferedString); 213 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); 214 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); 215 return EFI_SUCCESS; 216 } else { 217 // 218 // Simply create a popup to tell the user that they had typed in too few characters. 219 // To save code space, we can then treat this as an error and return back to the menu. 220 // 221 do { 222 CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL); 223 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN); 224 225 FreePool (TempString); 226 FreePool (BufferedString); 227 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); 228 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible); 229 return EFI_DEVICE_ERROR; 230 } 231 232 233 case CHAR_BACKSPACE: 234 if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) { 235 for (Index = 0; Index < CurrentCursor - 1; Index++) { 236 TempString[Index] = StringPtr[Index]; 237 } 238 Count = GetStringWidth (StringPtr) / 2 - 1; 239 if (Count >= CurrentCursor) { 240 for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) { 241 TempString[Index] = StringPtr[Index2]; 242 } 243 TempString[Index] = CHAR_NULL; 244 } 245 // 246 // Effectively truncate string by 1 character 247 // 248 StrCpyS (StringPtr, MaxLen, TempString); 249 CurrentCursor --; 250 } 251 252 default: 253 // 254 // If it is the beginning of the string, don't worry about checking maximum limits 255 // 256 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) { 257 StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1); 258 CurrentCursor++; 259 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) { 260 KeyPad[0] = Key.UnicodeChar; 261 KeyPad[1] = CHAR_NULL; 262 Count = GetStringWidth (StringPtr) / 2 - 1; 263 if (CurrentCursor < Count) { 264 for (Index = 0; Index < CurrentCursor; Index++) { 265 TempString[Index] = StringPtr[Index]; 266 } 267 TempString[Index] = CHAR_NULL; 268 StrCatS (TempString, MaxLen, KeyPad); 269 StrCatS (TempString, MaxLen, StringPtr + CurrentCursor); 270 StrCpyS (StringPtr, MaxLen, TempString); 271 } else { 272 StrCatS (StringPtr, MaxLen, KeyPad); 273 } 274 CurrentCursor++; 275 } 276 277 // 278 // If the width of the input string is now larger than the screen, we nee to 279 // adjust the index to start printing portions of the string 280 // 281 SetUnicodeMem (BufferedString, ScreenSize - 1, L' '); 282 PrintStringAt (Start + 1, Top + 3, BufferedString); 283 284 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) { 285 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2; 286 } else { 287 Index = 0; 288 } 289 290 if (IsPassword) { 291 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3); 292 } 293 294 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) { 295 BufferedString[Count] = StringPtr[Index]; 296 297 if (IsPassword) { 298 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*'); 299 } 300 } 301 302 if (!IsPassword) { 303 PrintStringAt (Start + 1, Top + 3, BufferedString); 304 } 305 break; 306 } 307 308 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK)); 309 gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3); 310 } while (TRUE); 311 312 } 313 314 /** 315 Adjust the value to the correct one. Rules follow the sample: 316 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01 317 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28 318 319 @param QuestionValue Pointer to current question. 320 @param Sequence The sequence of the field in the question. 321 **/ 322 VOID 323 AdjustQuestionValue ( 324 IN EFI_HII_VALUE *QuestionValue, 325 IN UINT8 Sequence 326 ) 327 { 328 UINT8 Month; 329 UINT16 Year; 330 UINT8 Maximum; 331 UINT8 Minimum; 332 333 Month = QuestionValue->Value.date.Month; 334 Year = QuestionValue->Value.date.Year; 335 Minimum = 1; 336 337 switch (Month) { 338 case 2: 339 if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) { 340 Maximum = 29; 341 } else { 342 Maximum = 28; 343 } 344 break; 345 case 4: 346 case 6: 347 case 9: 348 case 11: 349 Maximum = 30; 350 break; 351 default: 352 Maximum = 31; 353 break; 354 } 355 356 // 357 // Change the month area. 358 // 359 if (Sequence == 0) { 360 if (QuestionValue->Value.date.Day > Maximum) { 361 QuestionValue->Value.date.Day = Maximum; 362 } 363 } 364 365 // 366 // Change the Year area. 367 // 368 if (Sequence == 2) { 369 if (QuestionValue->Value.date.Day > Maximum) { 370 QuestionValue->Value.date.Day = Minimum; 371 } 372 } 373 } 374 375 /** 376 Get field info from numeric opcode. 377 378 @param OpCode Pointer to the current input opcode. 379 @param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type. 380 @param QuestionValue Input question value, with EFI_HII_VALUE type. 381 @param Value Return question value, always return UINT64 type. 382 @param Minimum The minimum size info for this opcode. 383 @param Maximum The maximum size info for this opcode. 384 @param Step The step size info for this opcode. 385 @param StorageWidth The storage width info for this opcode. 386 387 **/ 388 VOID 389 GetValueFromNum ( 390 IN EFI_IFR_OP_HEADER *OpCode, 391 IN BOOLEAN IntInput, 392 IN EFI_HII_VALUE *QuestionValue, 393 OUT UINT64 *Value, 394 OUT UINT64 *Minimum, 395 OUT UINT64 *Maximum, 396 OUT UINT64 *Step, 397 OUT UINT16 *StorageWidth 398 ) 399 { 400 EFI_IFR_NUMERIC *NumericOp; 401 402 NumericOp = (EFI_IFR_NUMERIC *) OpCode; 403 404 switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) { 405 case EFI_IFR_NUMERIC_SIZE_1: 406 if (IntInput) { 407 *Minimum = (INT64) (INT8) NumericOp->data.u8.MinValue; 408 *Maximum = (INT64) (INT8) NumericOp->data.u8.MaxValue; 409 *Value = (INT64) (INT8) QuestionValue->Value.u8; 410 } else { 411 *Minimum = NumericOp->data.u8.MinValue; 412 *Maximum = NumericOp->data.u8.MaxValue; 413 *Value = QuestionValue->Value.u8; 414 } 415 *Step = NumericOp->data.u8.Step; 416 *StorageWidth = (UINT16) sizeof (UINT8); 417 break; 418 419 case EFI_IFR_NUMERIC_SIZE_2: 420 if (IntInput) { 421 *Minimum = (INT64) (INT16) NumericOp->data.u16.MinValue; 422 *Maximum = (INT64) (INT16) NumericOp->data.u16.MaxValue; 423 *Value = (INT64) (INT16) QuestionValue->Value.u16; 424 } else { 425 *Minimum = NumericOp->data.u16.MinValue; 426 *Maximum = NumericOp->data.u16.MaxValue; 427 *Value = QuestionValue->Value.u16; 428 } 429 *Step = NumericOp->data.u16.Step; 430 *StorageWidth = (UINT16) sizeof (UINT16); 431 break; 432 433 case EFI_IFR_NUMERIC_SIZE_4: 434 if (IntInput) { 435 *Minimum = (INT64) (INT32) NumericOp->data.u32.MinValue; 436 *Maximum = (INT64) (INT32) NumericOp->data.u32.MaxValue; 437 *Value = (INT64) (INT32) QuestionValue->Value.u32; 438 } else { 439 *Minimum = NumericOp->data.u32.MinValue; 440 *Maximum = NumericOp->data.u32.MaxValue; 441 *Value = QuestionValue->Value.u32; 442 } 443 *Step = NumericOp->data.u32.Step; 444 *StorageWidth = (UINT16) sizeof (UINT32); 445 break; 446 447 case EFI_IFR_NUMERIC_SIZE_8: 448 if (IntInput) { 449 *Minimum = (INT64) NumericOp->data.u64.MinValue; 450 *Maximum = (INT64) NumericOp->data.u64.MaxValue; 451 *Value = (INT64) QuestionValue->Value.u64; 452 } else { 453 *Minimum = NumericOp->data.u64.MinValue; 454 *Maximum = NumericOp->data.u64.MaxValue; 455 *Value = QuestionValue->Value.u64; 456 } 457 *Step = NumericOp->data.u64.Step; 458 *StorageWidth = (UINT16) sizeof (UINT64); 459 break; 460 461 default: 462 break; 463 } 464 465 if (*Maximum == 0) { 466 *Maximum = (UINT64) -1; 467 } 468 } 469 470 /** 471 This routine reads a numeric value from the user input. 472 473 @param MenuOption Pointer to the current input menu. 474 475 @retval EFI_SUCCESS If numerical input is read successfully 476 @retval EFI_DEVICE_ERROR If operation fails 477 478 **/ 479 EFI_STATUS 480 GetNumericInput ( 481 IN UI_MENU_OPTION *MenuOption 482 ) 483 { 484 UINTN Column; 485 UINTN Row; 486 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH]; 487 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1]; 488 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3]; 489 UINTN Count; 490 UINTN Loop; 491 BOOLEAN ManualInput; 492 BOOLEAN HexInput; 493 BOOLEAN IntInput; 494 BOOLEAN Negative; 495 BOOLEAN ValidateFail; 496 BOOLEAN DateOrTime; 497 UINTN InputWidth; 498 UINT64 EditValue; 499 UINT64 Step; 500 UINT64 Minimum; 501 UINT64 Maximum; 502 UINTN EraseLen; 503 UINT8 Digital; 504 EFI_INPUT_KEY Key; 505 EFI_HII_VALUE *QuestionValue; 506 FORM_DISPLAY_ENGINE_STATEMENT *Question; 507 EFI_IFR_NUMERIC *NumericOp; 508 UINT16 StorageWidth; 509 510 Column = MenuOption->OptCol; 511 Row = MenuOption->Row; 512 PreviousNumber[0] = 0; 513 Count = 0; 514 InputWidth = 0; 515 Digital = 0; 516 StorageWidth = 0; 517 Minimum = 0; 518 Maximum = 0; 519 NumericOp = NULL; 520 IntInput = FALSE; 521 HexInput = FALSE; 522 Negative = FALSE; 523 ValidateFail = FALSE; 524 525 Question = MenuOption->ThisTag; 526 QuestionValue = &Question->CurrentValue; 527 528 // 529 // Only two case, user can enter to this function: Enter and +/- case. 530 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT 531 // 532 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE); 533 534 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) { 535 DateOrTime = TRUE; 536 } else { 537 DateOrTime = FALSE; 538 } 539 540 // 541 // Prepare Value to be edit 542 // 543 EraseLen = 0; 544 EditValue = 0; 545 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { 546 Step = 1; 547 Minimum = 1; 548 549 switch (MenuOption->Sequence) { 550 case 0: 551 Maximum = 12; 552 EraseLen = 4; 553 EditValue = QuestionValue->Value.date.Month; 554 break; 555 556 case 1: 557 switch (QuestionValue->Value.date.Month) { 558 case 2: 559 if ((QuestionValue->Value.date.Year % 4) == 0 && 560 ((QuestionValue->Value.date.Year % 100) != 0 || 561 (QuestionValue->Value.date.Year % 400) == 0)) { 562 Maximum = 29; 563 } else { 564 Maximum = 28; 565 } 566 break; 567 case 4: 568 case 6: 569 case 9: 570 case 11: 571 Maximum = 30; 572 break; 573 default: 574 Maximum = 31; 575 break; 576 } 577 578 EraseLen = 3; 579 EditValue = QuestionValue->Value.date.Day; 580 break; 581 582 case 2: 583 Maximum = 0xffff; 584 EraseLen = 5; 585 EditValue = QuestionValue->Value.date.Year; 586 break; 587 588 default: 589 break; 590 } 591 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { 592 Step = 1; 593 Minimum = 0; 594 595 switch (MenuOption->Sequence) { 596 case 0: 597 Maximum = 23; 598 EraseLen = 4; 599 EditValue = QuestionValue->Value.time.Hour; 600 break; 601 602 case 1: 603 Maximum = 59; 604 EraseLen = 3; 605 EditValue = QuestionValue->Value.time.Minute; 606 break; 607 608 case 2: 609 Maximum = 59; 610 EraseLen = 3; 611 EditValue = QuestionValue->Value.time.Second; 612 break; 613 614 default: 615 break; 616 } 617 } else { 618 ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP); 619 NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode; 620 GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth); 621 EraseLen = gOptionBlockWidth; 622 } 623 624 if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) { 625 if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){ 626 HexInput = TRUE; 627 } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){ 628 // 629 // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number. 630 // 631 IntInput = TRUE; 632 } 633 } 634 635 // 636 // Enter from "Enter" input, clear the old word showing. 637 // 638 if (ManualInput) { 639 if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) { 640 if (HexInput) { 641 InputWidth = StorageWidth * 2; 642 } else { 643 switch (StorageWidth) { 644 case 1: 645 InputWidth = 3; 646 break; 647 648 case 2: 649 InputWidth = 5; 650 break; 651 652 case 4: 653 InputWidth = 10; 654 break; 655 656 case 8: 657 InputWidth = 20; 658 break; 659 660 default: 661 InputWidth = 0; 662 break; 663 } 664 665 if (IntInput) { 666 // 667 // Support an extra '-' for negative number. 668 // 669 InputWidth += 1; 670 } 671 } 672 673 InputText[0] = LEFT_NUMERIC_DELIMITER; 674 SetUnicodeMem (InputText + 1, InputWidth, L' '); 675 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH); 676 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; 677 InputText[InputWidth + 2] = L'\0'; 678 679 PrintStringAt (Column, Row, InputText); 680 Column++; 681 } 682 683 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { 684 if (MenuOption->Sequence == 2) { 685 InputWidth = 4; 686 } else { 687 InputWidth = 2; 688 } 689 690 if (MenuOption->Sequence == 0) { 691 InputText[0] = LEFT_NUMERIC_DELIMITER; 692 SetUnicodeMem (InputText + 1, InputWidth, L' '); 693 } else { 694 SetUnicodeMem (InputText, InputWidth, L' '); 695 } 696 697 if (MenuOption->Sequence == 2) { 698 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; 699 } else { 700 InputText[InputWidth + 1] = DATE_SEPARATOR; 701 } 702 InputText[InputWidth + 2] = L'\0'; 703 704 PrintStringAt (Column, Row, InputText); 705 if (MenuOption->Sequence == 0) { 706 Column++; 707 } 708 } 709 710 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { 711 InputWidth = 2; 712 713 if (MenuOption->Sequence == 0) { 714 InputText[0] = LEFT_NUMERIC_DELIMITER; 715 SetUnicodeMem (InputText + 1, InputWidth, L' '); 716 } else { 717 SetUnicodeMem (InputText, InputWidth, L' '); 718 } 719 720 if (MenuOption->Sequence == 2) { 721 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER; 722 } else { 723 InputText[InputWidth + 1] = TIME_SEPARATOR; 724 } 725 InputText[InputWidth + 2] = L'\0'; 726 727 PrintStringAt (Column, Row, InputText); 728 if (MenuOption->Sequence == 0) { 729 Column++; 730 } 731 } 732 } 733 734 // 735 // First time we enter this handler, we need to check to see if 736 // we were passed an increment or decrement directive 737 // 738 do { 739 Key.UnicodeChar = CHAR_NULL; 740 if (gDirection != 0) { 741 Key.ScanCode = gDirection; 742 gDirection = 0; 743 goto TheKey2; 744 } 745 746 WaitForKeyStroke (&Key); 747 748 TheKey2: 749 switch (Key.UnicodeChar) { 750 751 case '+': 752 case '-': 753 if (ManualInput && IntInput) { 754 // 755 // In Manual input mode, check whether input the negative flag. 756 // 757 if (Key.UnicodeChar == '-') { 758 if (Negative) { 759 break; 760 } 761 Negative = TRUE; 762 PrintCharAt (Column++, Row, Key.UnicodeChar); 763 } 764 } else { 765 if (Key.UnicodeChar == '+') { 766 Key.ScanCode = SCAN_RIGHT; 767 } else { 768 Key.ScanCode = SCAN_LEFT; 769 } 770 Key.UnicodeChar = CHAR_NULL; 771 goto TheKey2; 772 } 773 break; 774 775 case CHAR_NULL: 776 switch (Key.ScanCode) { 777 case SCAN_LEFT: 778 case SCAN_RIGHT: 779 if (DateOrTime && !ManualInput) { 780 // 781 // By setting this value, we will return back to the caller. 782 // We need to do this since an auto-refresh will destroy the adjustment 783 // based on what the real-time-clock is showing. So we always commit 784 // upon changing the value. 785 // 786 gDirection = SCAN_DOWN; 787 } 788 789 if ((Step != 0) && !ManualInput) { 790 if (Key.ScanCode == SCAN_LEFT) { 791 if (IntInput) { 792 if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) { 793 EditValue = EditValue - Step; 794 } else if ((INT64) EditValue > (INT64) Minimum){ 795 EditValue = Minimum; 796 } else { 797 EditValue = Maximum; 798 } 799 } else { 800 if (EditValue >= Minimum + Step) { 801 EditValue = EditValue - Step; 802 } else if (EditValue > Minimum){ 803 EditValue = Minimum; 804 } else { 805 EditValue = Maximum; 806 } 807 } 808 } else if (Key.ScanCode == SCAN_RIGHT) { 809 if (IntInput) { 810 if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) { 811 EditValue = EditValue + Step; 812 } else if ((INT64) EditValue < (INT64) Maximum) { 813 EditValue = Maximum; 814 } else { 815 EditValue = Minimum; 816 } 817 } else { 818 if (EditValue + Step <= Maximum) { 819 EditValue = EditValue + Step; 820 } else if (EditValue < Maximum) { 821 EditValue = Maximum; 822 } else { 823 EditValue = Minimum; 824 } 825 } 826 } 827 828 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16)); 829 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { 830 if (MenuOption->Sequence == 2) { 831 // 832 // Year 833 // 834 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue); 835 } else { 836 // 837 // Month/Day 838 // 839 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); 840 } 841 842 if (MenuOption->Sequence == 0) { 843 ASSERT (EraseLen >= 2); 844 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR; 845 } else if (MenuOption->Sequence == 1) { 846 ASSERT (EraseLen >= 1); 847 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR; 848 } 849 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { 850 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue); 851 852 if (MenuOption->Sequence == 0) { 853 ASSERT (EraseLen >= 2); 854 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR; 855 } else if (MenuOption->Sequence == 1) { 856 ASSERT (EraseLen >= 1); 857 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR; 858 } 859 } else { 860 QuestionValue->Value.u64 = EditValue; 861 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16)); 862 } 863 864 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ()); 865 for (Loop = 0; Loop < EraseLen; Loop++) { 866 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" "); 867 } 868 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); 869 870 if (MenuOption->Sequence == 0) { 871 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER); 872 Column = MenuOption->OptCol + 1; 873 } 874 875 PrintStringAt (Column, Row, FormattedNumber); 876 877 if (!DateOrTime || MenuOption->Sequence == 2) { 878 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER); 879 } 880 } 881 882 goto EnterCarriageReturn; 883 884 case SCAN_UP: 885 case SCAN_DOWN: 886 goto EnterCarriageReturn; 887 888 case SCAN_ESC: 889 return EFI_DEVICE_ERROR; 890 891 default: 892 break; 893 } 894 895 break; 896 897 EnterCarriageReturn: 898 899 case CHAR_CARRIAGE_RETURN: 900 // 901 // Validate input value with Minimum value. 902 // 903 ValidateFail = FALSE; 904 if (IntInput) { 905 // 906 // After user input Enter, need to check whether the input value. 907 // If input a negative value, should compare with maximum value. 908 // else compare with the minimum value. 909 // 910 if (Negative) { 911 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; 912 } else { 913 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; 914 } 915 916 if (ValidateFail) { 917 UpdateStatusBar (INPUT_ERROR, TRUE); 918 break; 919 } 920 } else if (EditValue < Minimum) { 921 UpdateStatusBar (INPUT_ERROR, TRUE); 922 break; 923 } 924 925 UpdateStatusBar (INPUT_ERROR, FALSE); 926 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE)); 927 QuestionValue = &gUserInput->InputValue; 928 // 929 // Store Edit value back to Question 930 // 931 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) { 932 switch (MenuOption->Sequence) { 933 case 0: 934 QuestionValue->Value.date.Month = (UINT8) EditValue; 935 break; 936 937 case 1: 938 QuestionValue->Value.date.Day = (UINT8) EditValue; 939 break; 940 941 case 2: 942 QuestionValue->Value.date.Year = (UINT16) EditValue; 943 break; 944 945 default: 946 break; 947 } 948 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) { 949 switch (MenuOption->Sequence) { 950 case 0: 951 QuestionValue->Value.time.Hour = (UINT8) EditValue; 952 break; 953 954 case 1: 955 QuestionValue->Value.time.Minute = (UINT8) EditValue; 956 break; 957 958 case 2: 959 QuestionValue->Value.time.Second = (UINT8) EditValue; 960 break; 961 962 default: 963 break; 964 } 965 } else { 966 // 967 // Numeric 968 // 969 QuestionValue->Value.u64 = EditValue; 970 } 971 972 // 973 // Adjust the value to the correct one. 974 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01 975 // 2013.03.29 -> 2013.02.29 -> 2013.02.28 976 // 977 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP && 978 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) { 979 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence); 980 } 981 982 return EFI_SUCCESS; 983 984 case CHAR_BACKSPACE: 985 if (ManualInput) { 986 if (Count == 0) { 987 if (Negative) { 988 Negative = FALSE; 989 Column--; 990 PrintStringAt (Column, Row, L" "); 991 } 992 break; 993 } 994 // 995 // Remove a character 996 // 997 EditValue = PreviousNumber[Count - 1]; 998 UpdateStatusBar (INPUT_ERROR, FALSE); 999 Count--; 1000 Column--; 1001 PrintStringAt (Column, Row, L" "); 1002 } 1003 break; 1004 1005 default: 1006 if (ManualInput) { 1007 if (HexInput) { 1008 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) { 1009 Digital = (UINT8) (Key.UnicodeChar - L'0'); 1010 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) { 1011 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A); 1012 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) { 1013 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A); 1014 } else { 1015 UpdateStatusBar (INPUT_ERROR, TRUE); 1016 break; 1017 } 1018 } else { 1019 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') { 1020 UpdateStatusBar (INPUT_ERROR, TRUE); 1021 break; 1022 } 1023 } 1024 1025 // 1026 // If Count exceed input width, there is no way more is valid 1027 // 1028 if (Count >= InputWidth) { 1029 break; 1030 } 1031 // 1032 // Someone typed something valid! 1033 // 1034 if (Count != 0) { 1035 if (HexInput) { 1036 EditValue = LShiftU64 (EditValue, 4) + Digital; 1037 } else if (IntInput && Negative) { 1038 // 1039 // Save the negative number. 1040 // 1041 EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1; 1042 } else { 1043 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0'); 1044 } 1045 } else { 1046 if (HexInput) { 1047 EditValue = Digital; 1048 } else if (IntInput && Negative) { 1049 // 1050 // Save the negative number. 1051 // 1052 EditValue = ~(Key.UnicodeChar - L'0') + 1; 1053 } else { 1054 EditValue = Key.UnicodeChar - L'0'; 1055 } 1056 } 1057 1058 if (IntInput) { 1059 ValidateFail = FALSE; 1060 // 1061 // When user input a new value, should check the current value. 1062 // If user input a negative value, should compare it with minimum 1063 // value, else compare it with maximum value. 1064 // 1065 if (Negative) { 1066 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE; 1067 } else { 1068 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE; 1069 } 1070 1071 if (ValidateFail) { 1072 UpdateStatusBar (INPUT_ERROR, TRUE); 1073 ASSERT (Count < ARRAY_SIZE (PreviousNumber)); 1074 EditValue = PreviousNumber[Count]; 1075 break; 1076 } 1077 } else { 1078 if (EditValue > Maximum) { 1079 UpdateStatusBar (INPUT_ERROR, TRUE); 1080 ASSERT (Count < ARRAY_SIZE (PreviousNumber)); 1081 EditValue = PreviousNumber[Count]; 1082 break; 1083 } 1084 } 1085 1086 UpdateStatusBar (INPUT_ERROR, FALSE); 1087 1088 Count++; 1089 ASSERT (Count < (ARRAY_SIZE (PreviousNumber))); 1090 PreviousNumber[Count] = EditValue; 1091 1092 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ()); 1093 PrintCharAt (Column, Row, Key.UnicodeChar); 1094 Column++; 1095 } 1096 break; 1097 } 1098 } while (TRUE); 1099 } 1100 1101 /** 1102 Adjust option order base on the question value. 1103 1104 @param Question Pointer to current question. 1105 @param PopUpMenuLines The line number of the pop up menu. 1106 1107 @retval EFI_SUCCESS If Option input is processed successfully 1108 @retval EFI_DEVICE_ERROR If operation fails 1109 1110 **/ 1111 EFI_STATUS 1112 AdjustOptionOrder ( 1113 IN FORM_DISPLAY_ENGINE_STATEMENT *Question, 1114 OUT UINTN *PopUpMenuLines 1115 ) 1116 { 1117 UINTN Index; 1118 EFI_IFR_ORDERED_LIST *OrderList; 1119 UINT8 *ValueArray; 1120 UINT8 ValueType; 1121 LIST_ENTRY *Link; 1122 DISPLAY_QUESTION_OPTION *OneOfOption; 1123 EFI_HII_VALUE *HiiValueArray; 1124 1125 Link = GetFirstNode (&Question->OptionListHead); 1126 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1127 ValueArray = Question->CurrentValue.Buffer; 1128 ValueType = OneOfOption->OptionOpCode->Type; 1129 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; 1130 1131 for (Index = 0; Index < OrderList->MaxContainers; Index++) { 1132 if (GetArrayData (ValueArray, ValueType, Index) == 0) { 1133 break; 1134 } 1135 } 1136 1137 *PopUpMenuLines = Index; 1138 1139 // 1140 // Prepare HiiValue array 1141 // 1142 HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE)); 1143 ASSERT (HiiValueArray != NULL); 1144 1145 for (Index = 0; Index < *PopUpMenuLines; Index++) { 1146 HiiValueArray[Index].Type = ValueType; 1147 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index); 1148 } 1149 1150 for (Index = 0; Index < *PopUpMenuLines; Index++) { 1151 OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]); 1152 if (OneOfOption == NULL) { 1153 return EFI_NOT_FOUND; 1154 } 1155 1156 RemoveEntryList (&OneOfOption->Link); 1157 1158 // 1159 // Insert to head. 1160 // 1161 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link); 1162 } 1163 1164 FreePool (HiiValueArray); 1165 1166 return EFI_SUCCESS; 1167 } 1168 1169 /** 1170 Base on the type to compare the value. 1171 1172 @param Value1 The first value need to compare. 1173 @param Value2 The second value need to compare. 1174 @param Type The value type for above two values. 1175 1176 @retval TRUE The two value are same. 1177 @retval FALSE The two value are different. 1178 1179 **/ 1180 BOOLEAN 1181 IsValuesEqual ( 1182 IN EFI_IFR_TYPE_VALUE *Value1, 1183 IN EFI_IFR_TYPE_VALUE *Value2, 1184 IN UINT8 Type 1185 ) 1186 { 1187 switch (Type) { 1188 case EFI_IFR_TYPE_BOOLEAN: 1189 case EFI_IFR_TYPE_NUM_SIZE_8: 1190 return (BOOLEAN) (Value1->u8 == Value2->u8); 1191 1192 case EFI_IFR_TYPE_NUM_SIZE_16: 1193 return (BOOLEAN) (Value1->u16 == Value2->u16); 1194 1195 case EFI_IFR_TYPE_NUM_SIZE_32: 1196 return (BOOLEAN) (Value1->u32 == Value2->u32); 1197 1198 case EFI_IFR_TYPE_NUM_SIZE_64: 1199 return (BOOLEAN) (Value1->u64 == Value2->u64); 1200 1201 default: 1202 ASSERT (FALSE); 1203 return FALSE; 1204 } 1205 } 1206 1207 /** 1208 Base on the type to set the value. 1209 1210 @param Dest The dest value. 1211 @param Source The source value. 1212 @param Type The value type for above two values. 1213 1214 **/ 1215 VOID 1216 SetValuesByType ( 1217 OUT EFI_IFR_TYPE_VALUE *Dest, 1218 IN EFI_IFR_TYPE_VALUE *Source, 1219 IN UINT8 Type 1220 ) 1221 { 1222 switch (Type) { 1223 case EFI_IFR_TYPE_BOOLEAN: 1224 Dest->b = Source->b; 1225 break; 1226 1227 case EFI_IFR_TYPE_NUM_SIZE_8: 1228 Dest->u8 = Source->u8; 1229 break; 1230 1231 case EFI_IFR_TYPE_NUM_SIZE_16: 1232 Dest->u16 = Source->u16; 1233 break; 1234 1235 case EFI_IFR_TYPE_NUM_SIZE_32: 1236 Dest->u32 = Source->u32; 1237 break; 1238 1239 case EFI_IFR_TYPE_NUM_SIZE_64: 1240 Dest->u64 = Source->u64; 1241 break; 1242 1243 default: 1244 ASSERT (FALSE); 1245 break; 1246 } 1247 } 1248 1249 /** 1250 Get selection for OneOf and OrderedList (Left/Right will be ignored). 1251 1252 @param MenuOption Pointer to the current input menu. 1253 1254 @retval EFI_SUCCESS If Option input is processed successfully 1255 @retval EFI_DEVICE_ERROR If operation fails 1256 1257 **/ 1258 EFI_STATUS 1259 GetSelectionInputPopUp ( 1260 IN UI_MENU_OPTION *MenuOption 1261 ) 1262 { 1263 EFI_INPUT_KEY Key; 1264 UINTN Index; 1265 CHAR16 *StringPtr; 1266 CHAR16 *TempStringPtr; 1267 UINTN Index2; 1268 UINTN TopOptionIndex; 1269 UINTN HighlightOptionIndex; 1270 UINTN Start; 1271 UINTN End; 1272 UINTN Top; 1273 UINTN Bottom; 1274 UINTN PopUpMenuLines; 1275 UINTN MenuLinesInView; 1276 UINTN PopUpWidth; 1277 CHAR16 Character; 1278 INT32 SavedAttribute; 1279 BOOLEAN ShowDownArrow; 1280 BOOLEAN ShowUpArrow; 1281 UINTN DimensionsWidth; 1282 LIST_ENTRY *Link; 1283 BOOLEAN OrderedList; 1284 UINT8 *ValueArray; 1285 UINT8 *ReturnValue; 1286 UINT8 ValueType; 1287 EFI_HII_VALUE HiiValue; 1288 DISPLAY_QUESTION_OPTION *OneOfOption; 1289 DISPLAY_QUESTION_OPTION *CurrentOption; 1290 FORM_DISPLAY_ENGINE_STATEMENT *Question; 1291 INTN Result; 1292 EFI_IFR_ORDERED_LIST *OrderList; 1293 1294 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn; 1295 1296 ValueArray = NULL; 1297 ValueType = 0; 1298 CurrentOption = NULL; 1299 ShowDownArrow = FALSE; 1300 ShowUpArrow = FALSE; 1301 1302 ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE)); 1303 1304 Question = MenuOption->ThisTag; 1305 if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) { 1306 Link = GetFirstNode (&Question->OptionListHead); 1307 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1308 ValueArray = Question->CurrentValue.Buffer; 1309 ValueType = OneOfOption->OptionOpCode->Type; 1310 OrderedList = TRUE; 1311 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode; 1312 } else { 1313 OrderedList = FALSE; 1314 OrderList = NULL; 1315 } 1316 1317 // 1318 // Calculate Option count 1319 // 1320 PopUpMenuLines = 0; 1321 if (OrderedList) { 1322 AdjustOptionOrder(Question, &PopUpMenuLines); 1323 } else { 1324 Link = GetFirstNode (&Question->OptionListHead); 1325 while (!IsNull (&Question->OptionListHead, Link)) { 1326 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1327 PopUpMenuLines++; 1328 Link = GetNextNode (&Question->OptionListHead, Link); 1329 } 1330 } 1331 1332 // 1333 // Get the number of one of options present and its size 1334 // 1335 PopUpWidth = 0; 1336 HighlightOptionIndex = 0; 1337 Link = GetFirstNode (&Question->OptionListHead); 1338 for (Index = 0; Index < PopUpMenuLines; Index++) { 1339 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1340 1341 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); 1342 if (StrLen (StringPtr) > PopUpWidth) { 1343 PopUpWidth = StrLen (StringPtr); 1344 } 1345 FreePool (StringPtr); 1346 HiiValue.Type = OneOfOption->OptionOpCode->Type; 1347 SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type); 1348 if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) { 1349 // 1350 // Find current selected Option for OneOf 1351 // 1352 HighlightOptionIndex = Index; 1353 } 1354 1355 Link = GetNextNode (&Question->OptionListHead, Link); 1356 } 1357 1358 // 1359 // Perform popup menu initialization. 1360 // 1361 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT; 1362 1363 SavedAttribute = gST->ConOut->Mode->Attribute; 1364 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); 1365 1366 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) { 1367 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH; 1368 } 1369 1370 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn; 1371 End = Start + PopUpWidth + POPUP_FRAME_WIDTH; 1372 Top = gStatementDimensions.TopRow; 1373 Bottom = gStatementDimensions.BottomRow - 1; 1374 1375 MenuLinesInView = Bottom - Top - 1; 1376 if (MenuLinesInView >= PopUpMenuLines) { 1377 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2; 1378 Bottom = Top + PopUpMenuLines + 1; 1379 } else { 1380 ShowDownArrow = TRUE; 1381 } 1382 1383 if (HighlightOptionIndex > (MenuLinesInView - 1)) { 1384 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1; 1385 } else { 1386 TopOptionIndex = 0; 1387 } 1388 1389 do { 1390 // 1391 // Clear that portion of the screen 1392 // 1393 ClearLines (Start, End, Top, Bottom, GetPopupColor ()); 1394 1395 // 1396 // Draw "One of" pop-up menu 1397 // 1398 Character = BOXDRAW_DOWN_RIGHT; 1399 PrintCharAt (Start, Top, Character); 1400 for (Index = Start; Index + 2 < End; Index++) { 1401 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) { 1402 Character = GEOMETRICSHAPE_UP_TRIANGLE; 1403 } else { 1404 Character = BOXDRAW_HORIZONTAL; 1405 } 1406 1407 PrintCharAt ((UINTN)-1, (UINTN)-1, Character); 1408 } 1409 1410 Character = BOXDRAW_DOWN_LEFT; 1411 PrintCharAt ((UINTN)-1, (UINTN)-1, Character); 1412 Character = BOXDRAW_VERTICAL; 1413 for (Index = Top + 1; Index < Bottom; Index++) { 1414 PrintCharAt (Start, Index, Character); 1415 PrintCharAt (End - 1, Index, Character); 1416 } 1417 1418 // 1419 // Move to top Option 1420 // 1421 Link = GetFirstNode (&Question->OptionListHead); 1422 for (Index = 0; Index < TopOptionIndex; Index++) { 1423 Link = GetNextNode (&Question->OptionListHead, Link); 1424 } 1425 1426 // 1427 // Display the One of options 1428 // 1429 Index2 = Top + 1; 1430 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) { 1431 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1432 Link = GetNextNode (&Question->OptionListHead, Link); 1433 1434 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle); 1435 ASSERT (StringPtr != NULL); 1436 // 1437 // If the string occupies multiple lines, truncate it to fit in one line, 1438 // and append a "..." for indication. 1439 // 1440 if (StrLen (StringPtr) > (PopUpWidth - 1)) { 1441 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1)); 1442 ASSERT ( TempStringPtr != NULL ); 1443 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5))); 1444 FreePool (StringPtr); 1445 StringPtr = TempStringPtr; 1446 StrCatS (StringPtr, PopUpWidth - 1, L"..."); 1447 } 1448 1449 if (Index == HighlightOptionIndex) { 1450 // 1451 // Highlight the selected one 1452 // 1453 CurrentOption = OneOfOption; 1454 1455 gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ()); 1456 PrintStringAt (Start + 2, Index2, StringPtr); 1457 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); 1458 } else { 1459 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ()); 1460 PrintStringAt (Start + 2, Index2, StringPtr); 1461 } 1462 1463 Index2++; 1464 FreePool (StringPtr); 1465 } 1466 1467 Character = BOXDRAW_UP_RIGHT; 1468 PrintCharAt (Start, Bottom, Character); 1469 for (Index = Start; Index + 2 < End; Index++) { 1470 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) { 1471 Character = GEOMETRICSHAPE_DOWN_TRIANGLE; 1472 } else { 1473 Character = BOXDRAW_HORIZONTAL; 1474 } 1475 1476 PrintCharAt ((UINTN)-1, (UINTN)-1, Character); 1477 } 1478 1479 Character = BOXDRAW_UP_LEFT; 1480 PrintCharAt ((UINTN)-1, (UINTN)-1, Character); 1481 1482 // 1483 // Get User selection 1484 // 1485 Key.UnicodeChar = CHAR_NULL; 1486 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) { 1487 Key.ScanCode = gDirection; 1488 gDirection = 0; 1489 goto TheKey; 1490 } 1491 1492 WaitForKeyStroke (&Key); 1493 1494 TheKey: 1495 switch (Key.UnicodeChar) { 1496 case '+': 1497 if (OrderedList) { 1498 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { 1499 // 1500 // Highlight reaches the top of the popup window, scroll one menu item. 1501 // 1502 TopOptionIndex--; 1503 ShowDownArrow = TRUE; 1504 } 1505 1506 if (TopOptionIndex == 0) { 1507 ShowUpArrow = FALSE; 1508 } 1509 1510 if (HighlightOptionIndex > 0) { 1511 HighlightOptionIndex--; 1512 1513 ASSERT (CurrentOption != NULL); 1514 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link); 1515 } 1516 } 1517 break; 1518 1519 case '-': 1520 // 1521 // If an ordered list op-code, we will allow for a popup of +/- keys 1522 // to create an ordered list of items 1523 // 1524 if (OrderedList) { 1525 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && 1526 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { 1527 // 1528 // Highlight reaches the bottom of the popup window, scroll one menu item. 1529 // 1530 TopOptionIndex++; 1531 ShowUpArrow = TRUE; 1532 } 1533 1534 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { 1535 ShowDownArrow = FALSE; 1536 } 1537 1538 if (HighlightOptionIndex < (PopUpMenuLines - 1)) { 1539 HighlightOptionIndex++; 1540 1541 ASSERT (CurrentOption != NULL); 1542 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink); 1543 } 1544 } 1545 break; 1546 1547 case CHAR_NULL: 1548 switch (Key.ScanCode) { 1549 case SCAN_UP: 1550 case SCAN_DOWN: 1551 if (Key.ScanCode == SCAN_UP) { 1552 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) { 1553 // 1554 // Highlight reaches the top of the popup window, scroll one menu item. 1555 // 1556 TopOptionIndex--; 1557 ShowDownArrow = TRUE; 1558 } 1559 1560 if (TopOptionIndex == 0) { 1561 ShowUpArrow = FALSE; 1562 } 1563 1564 if (HighlightOptionIndex > 0) { 1565 HighlightOptionIndex--; 1566 } 1567 } else { 1568 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) && 1569 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) { 1570 // 1571 // Highlight reaches the bottom of the popup window, scroll one menu item. 1572 // 1573 TopOptionIndex++; 1574 ShowUpArrow = TRUE; 1575 } 1576 1577 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) { 1578 ShowDownArrow = FALSE; 1579 } 1580 1581 if (HighlightOptionIndex < (PopUpMenuLines - 1)) { 1582 HighlightOptionIndex++; 1583 } 1584 } 1585 break; 1586 1587 case SCAN_ESC: 1588 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); 1589 1590 // 1591 // Restore link list order for orderedlist 1592 // 1593 if (OrderedList) { 1594 HiiValue.Type = ValueType; 1595 HiiValue.Value.u64 = 0; 1596 for (Index = 0; Index < OrderList->MaxContainers; Index++) { 1597 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index); 1598 if (HiiValue.Value.u64 == 0) { 1599 break; 1600 } 1601 1602 OneOfOption = ValueToOption (Question, &HiiValue); 1603 if (OneOfOption == NULL) { 1604 return EFI_NOT_FOUND; 1605 } 1606 1607 RemoveEntryList (&OneOfOption->Link); 1608 InsertTailList (&Question->OptionListHead, &OneOfOption->Link); 1609 } 1610 } 1611 1612 return EFI_DEVICE_ERROR; 1613 1614 default: 1615 break; 1616 } 1617 1618 break; 1619 1620 case CHAR_CARRIAGE_RETURN: 1621 // 1622 // return the current selection 1623 // 1624 if (OrderedList) { 1625 ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen); 1626 ASSERT (ReturnValue != NULL); 1627 Index = 0; 1628 Link = GetFirstNode (&Question->OptionListHead); 1629 while (!IsNull (&Question->OptionListHead, Link)) { 1630 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link); 1631 Link = GetNextNode (&Question->OptionListHead, Link); 1632 1633 SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64); 1634 1635 Index++; 1636 if (Index > OrderList->MaxContainers) { 1637 break; 1638 } 1639 } 1640 if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) { 1641 FreePool (ReturnValue); 1642 return EFI_DEVICE_ERROR; 1643 } else { 1644 gUserInput->InputValue.Buffer = ReturnValue; 1645 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen; 1646 } 1647 } else { 1648 ASSERT (CurrentOption != NULL); 1649 gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type; 1650 if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) { 1651 return EFI_DEVICE_ERROR; 1652 } else { 1653 SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type); 1654 } 1655 } 1656 1657 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute); 1658 1659 return EFI_SUCCESS; 1660 1661 default: 1662 break; 1663 } 1664 } while (TRUE); 1665 1666 } 1667 1668