]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
1) Add BufToHexString, HexStringToBuf and IsHexDigit to BaseLib.
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / InputHandler.c
1 /** @file
2
3 Copyright (c) 2004 - 2007, Intel Corporation
4 All rights reserved. This program and the accompanying materials
5 are licensed and made available under the terms and conditions of the BSD License
6 which accompanies this distribution. The full text of the license may be found at
7 http://opensource.org/licenses/bsd-license.php
8
9 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12 Module Name:
13
14 InputHandler.c
15
16 Abstract:
17
18 Implementation for handling user input from the User Interface
19
20 Revision History
21
22
23 **/
24
25 #include "Ui.h"
26 #include "Setup.h"
27
28
29 /**
30 Get string or password input from user.
31
32 @param MenuOption Pointer to the current input menu.
33 @param Prompt The prompt string shown on popup window.
34 @param StringPtr Destination for use input string.
35
36 @retval EFI_SUCCESS If string input is read successfully
37 @retval EFI_DEVICE_ERROR If operation fails
38
39 **/
40 EFI_STATUS
41 ReadString (
42 IN UI_MENU_OPTION *MenuOption,
43 IN CHAR16 *Prompt,
44 OUT CHAR16 *StringPtr
45 )
46 {
47 EFI_STATUS Status;
48 EFI_INPUT_KEY Key;
49 CHAR16 NullCharacter;
50 UINTN ScreenSize;
51 CHAR16 Space[2];
52 CHAR16 KeyPad[2];
53 CHAR16 *TempString;
54 CHAR16 *BufferedString;
55 UINTN Index;
56 UINTN Count;
57 UINTN Start;
58 UINTN Top;
59 UINTN DimensionsWidth;
60 UINTN DimensionsHeight;
61 BOOLEAN CursorVisible;
62 UINTN Minimum;
63 UINTN Maximum;
64 FORM_BROWSER_STATEMENT *Question;
65 BOOLEAN IsPassword;
66
67 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
68 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
69
70 NullCharacter = CHAR_NULL;
71 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);
72 Space[0] = L' ';
73 Space[1] = CHAR_NULL;
74
75 Question = MenuOption->ThisTag;
76 Minimum = (UINTN) Question->Minimum;
77 Maximum = (UINTN) Question->Maximum;
78
79 if (Question->Operand == EFI_IFR_PASSWORD_OP) {
80 IsPassword = TRUE;
81 } else {
82 IsPassword = FALSE;
83 }
84
85 TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));
86 ASSERT (TempString);
87
88 if (ScreenSize < (Maximum + 1)) {
89 ScreenSize = Maximum + 1;
90 }
91
92 if ((ScreenSize + 2) > DimensionsWidth) {
93 ScreenSize = DimensionsWidth - 2;
94 }
95
96 BufferedString = AllocateZeroPool (ScreenSize * 2);
97 ASSERT (BufferedString);
98
99 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;
100 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;
101
102 //
103 // Display prompt for string
104 //
105 CreatePopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);
106
107 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
108
109 CursorVisible = gST->ConOut->Mode->CursorVisible;
110 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
111
112 do {
113 Status = WaitForKeyStroke (&Key);
114 ASSERT_EFI_ERROR (Status);
115
116 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));
117 switch (Key.UnicodeChar) {
118 case CHAR_NULL:
119 switch (Key.ScanCode) {
120 case SCAN_LEFT:
121 break;
122
123 case SCAN_RIGHT:
124 break;
125
126 case SCAN_ESC:
127 gBS->FreePool (TempString);
128 gBS->FreePool (BufferedString);
129 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
130 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
131 return EFI_DEVICE_ERROR;
132
133 default:
134 break;
135 }
136
137 break;
138
139 case CHAR_CARRIAGE_RETURN:
140 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {
141
142 gBS->FreePool (TempString);
143 gBS->FreePool (BufferedString);
144 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
145 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
146 return EFI_SUCCESS;
147 } else {
148 //
149 // Simply create a popup to tell the user that they had typed in too few characters.
150 // To save code space, we can then treat this as an error and return back to the menu.
151 //
152 do {
153 CreateDialog (4, TRUE, 0, NULL, &Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);
154 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
155
156 gBS->FreePool (TempString);
157 gBS->FreePool (BufferedString);
158 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
159 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);
160 return EFI_DEVICE_ERROR;
161 }
162
163 break;
164
165 case CHAR_BACKSPACE:
166 if (StringPtr[0] != CHAR_NULL) {
167 for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {
168 TempString[Index] = StringPtr[Index];
169 }
170 //
171 // Effectively truncate string by 1 character
172 //
173 TempString[Index - 1] = CHAR_NULL;
174 StrCpy (StringPtr, TempString);
175 }
176
177 default:
178 //
179 // If it is the beginning of the string, don't worry about checking maximum limits
180 //
181 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
182 StrnCpy (StringPtr, &Key.UnicodeChar, 1);
183 StrnCpy (TempString, &Key.UnicodeChar, 1);
184 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
185 KeyPad[0] = Key.UnicodeChar;
186 KeyPad[1] = CHAR_NULL;
187 StrCat (StringPtr, KeyPad);
188 StrCat (TempString, KeyPad);
189 }
190
191 //
192 // If the width of the input string is now larger than the screen, we nee to
193 // adjust the index to start printing portions of the string
194 //
195 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
196 PrintStringAt (Start + 1, Top + 3, BufferedString);
197
198 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
199 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
200 } else {
201 Index = 0;
202 }
203
204 if (IsPassword) {
205 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
206 }
207
208 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
209 BufferedString[Count] = StringPtr[Index];
210
211 if (IsPassword) {
212 PrintChar (L'*');
213 }
214 }
215
216 if (!IsPassword) {
217 PrintStringAt (Start + 1, Top + 3, BufferedString);
218 }
219 break;
220 }
221
222 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
223 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);
224 } while (TRUE);
225
226 }
227
228
229 /**
230 This routine reads a numeric value from the user input.
231
232 @param Selection Pointer to current selection.
233 @param MenuOption Pointer to the current input menu.
234
235 @retval EFI_SUCCESS If numerical input is read successfully
236 @retval EFI_DEVICE_ERROR If operation fails
237
238 **/
239 EFI_STATUS
240 GetNumericInput (
241 IN UI_MENU_SELECTION *Selection,
242 IN UI_MENU_OPTION *MenuOption
243 )
244 {
245 EFI_STATUS Status;
246 UINTN Column;
247 UINTN Row;
248 CHAR16 InputText[23];
249 CHAR16 FormattedNumber[22];
250 UINT64 PreviousNumber[20];
251 UINTN Count;
252 UINTN Loop;
253 BOOLEAN ManualInput;
254 BOOLEAN HexInput;
255 BOOLEAN DateOrTime;
256 UINTN InputWidth;
257 UINT64 EditValue;
258 UINT64 Step;
259 UINT64 Minimum;
260 UINT64 Maximum;
261 UINTN EraseLen;
262 UINT8 Digital;
263 EFI_INPUT_KEY Key;
264 EFI_HII_VALUE *QuestionValue;
265 FORM_BROWSER_FORM *Form;
266 FORM_BROWSER_FORMSET *FormSet;
267 FORM_BROWSER_STATEMENT *Question;
268
269 Column = MenuOption->OptCol;
270 Row = MenuOption->Row;
271 PreviousNumber[0] = 0;
272 Count = 0;
273 InputWidth = 0;
274 Digital = 0;
275
276 FormSet = Selection->FormSet;
277 Form = Selection->Form;
278 Question = MenuOption->ThisTag;
279 QuestionValue = &Question->HiiValue;
280 Step = Question->Step;
281 Minimum = Question->Minimum;
282 Maximum = Question->Maximum;
283
284 if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {
285 DateOrTime = TRUE;
286 } else {
287 DateOrTime = FALSE;
288 }
289
290 //
291 // Prepare Value to be edit
292 //
293 EraseLen = 0;
294 EditValue = 0;
295 if (Question->Operand == EFI_IFR_DATE_OP) {
296 Step = 1;
297 Minimum = 1;
298
299 switch (MenuOption->Sequence) {
300 case 0:
301 Maximum = 12;
302 EraseLen = 4;
303 EditValue = QuestionValue->Value.date.Month;
304 break;
305
306 case 1:
307 Maximum = 31;
308 EraseLen = 3;
309 EditValue = QuestionValue->Value.date.Day;
310 break;
311
312 case 2:
313 Maximum = 0xffff;
314 EraseLen = 5;
315 EditValue = QuestionValue->Value.date.Year;
316 break;
317
318 default:
319 break;
320 }
321 } else if (Question->Operand == EFI_IFR_TIME_OP) {
322 Step = 1;
323 Minimum = 0;
324
325 switch (MenuOption->Sequence) {
326 case 0:
327 Maximum = 23;
328 EraseLen = 4;
329 EditValue = QuestionValue->Value.time.Hour;
330 break;
331
332 case 1:
333 Maximum = 59;
334 EraseLen = 3;
335 EditValue = QuestionValue->Value.time.Minute;
336 break;
337
338 case 2:
339 Maximum = 59;
340 EraseLen = 3;
341 EditValue = QuestionValue->Value.time.Second;
342 break;
343
344 default:
345 break;
346 }
347 } else {
348 //
349 // Numeric
350 //
351 EraseLen = gOptionBlockWidth;
352 EditValue = QuestionValue->Value.u64;
353 if (Maximum == 0) {
354 Maximum = (UINT64) -1;
355 }
356 }
357
358 if (Step == 0) {
359 ManualInput = TRUE;
360 } else {
361 ManualInput = FALSE;
362 }
363
364 if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&
365 ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
366 HexInput = TRUE;
367 } else {
368 HexInput = FALSE;
369 }
370
371 if (ManualInput) {
372 if (HexInput) {
373 InputWidth = Question->StorageWidth * 2;
374 } else {
375 switch (Question->StorageWidth) {
376 case 1:
377 InputWidth = 3;
378 break;
379
380 case 2:
381 InputWidth = 5;
382 break;
383
384 case 4:
385 InputWidth = 10;
386 break;
387
388 case 8:
389 InputWidth = 20;
390 break;
391
392 default:
393 InputWidth = 0;
394 break;
395 }
396 }
397
398 InputText[0] = LEFT_NUMERIC_DELIMITER;
399 SetUnicodeMem (InputText + 1, InputWidth, L' ');
400 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
401 InputText[InputWidth + 2] = L'\0';
402
403 PrintAt (Column, Row, InputText);
404 Column++;
405 }
406
407 //
408 // First time we enter this handler, we need to check to see if
409 // we were passed an increment or decrement directive
410 //
411 do {
412 Key.UnicodeChar = CHAR_NULL;
413 if (gDirection != 0) {
414 Key.ScanCode = gDirection;
415 gDirection = 0;
416 goto TheKey2;
417 }
418
419 Status = WaitForKeyStroke (&Key);
420
421 TheKey2:
422 switch (Key.UnicodeChar) {
423
424 case '+':
425 case '-':
426 if (Key.UnicodeChar == '+') {
427 Key.ScanCode = SCAN_RIGHT;
428 } else {
429 Key.ScanCode = SCAN_LEFT;
430 }
431 Key.UnicodeChar = CHAR_NULL;
432 goto TheKey2;
433
434 case CHAR_NULL:
435 switch (Key.ScanCode) {
436 case SCAN_LEFT:
437 case SCAN_RIGHT:
438 if (DateOrTime) {
439 //
440 // By setting this value, we will return back to the caller.
441 // We need to do this since an auto-refresh will destroy the adjustment
442 // based on what the real-time-clock is showing. So we always commit
443 // upon changing the value.
444 //
445 gDirection = SCAN_DOWN;
446 }
447
448 if (!ManualInput) {
449 if (Key.ScanCode == SCAN_LEFT) {
450 if (EditValue > Step) {
451 EditValue = EditValue - Step;
452 } else {
453 EditValue = Minimum;
454 }
455 } else if (Key.ScanCode == SCAN_RIGHT) {
456 EditValue = EditValue + Step;
457 if (EditValue > Maximum) {
458 EditValue = Maximum;
459 }
460 }
461
462 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
463 if (Question->Operand == EFI_IFR_DATE_OP) {
464 if (MenuOption->Sequence == 2) {
465 //
466 // Year
467 //
468 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", EditValue);
469 } else {
470 //
471 // Month/Day
472 //
473 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
474 }
475
476 if (MenuOption->Sequence == 0) {
477 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
478 } else if (MenuOption->Sequence == 1) {
479 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
480 }
481 } else if (Question->Operand == EFI_IFR_TIME_OP) {
482 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
483
484 if (MenuOption->Sequence == 0) {
485 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
486 } else if (MenuOption->Sequence == 1) {
487 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
488 }
489 } else {
490 QuestionValue->Value.u64 = EditValue;
491 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
492 }
493
494 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
495 for (Loop = 0; Loop < EraseLen; Loop++) {
496 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
497 }
498 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
499
500 if (MenuOption->Sequence == 0) {
501 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
502 Column = MenuOption->OptCol + 1;
503 }
504
505 PrintStringAt (Column, Row, FormattedNumber);
506
507 if (!DateOrTime || MenuOption->Sequence == 2) {
508 PrintChar (RIGHT_NUMERIC_DELIMITER);
509 }
510 }
511 break;
512
513 case SCAN_UP:
514 case SCAN_DOWN:
515 goto EnterCarriageReturn;
516
517 case SCAN_ESC:
518 return EFI_DEVICE_ERROR;
519
520 default:
521 break;
522 }
523
524 break;
525
526 EnterCarriageReturn:
527
528 case CHAR_CARRIAGE_RETURN:
529 //
530 // Store Edit value back to Question
531 //
532 if (Question->Operand == EFI_IFR_DATE_OP) {
533 switch (MenuOption->Sequence) {
534 case 0:
535 QuestionValue->Value.date.Month = (UINT8) EditValue;
536 break;
537
538 case 1:
539 QuestionValue->Value.date.Day = (UINT8) EditValue;
540 break;
541
542 case 2:
543 QuestionValue->Value.date.Year = (UINT16) EditValue;
544 break;
545
546 default:
547 break;
548 }
549 } else if (Question->Operand == EFI_IFR_TIME_OP) {
550 switch (MenuOption->Sequence) {
551 case 0:
552 QuestionValue->Value.time.Hour = (UINT8) EditValue;
553 break;
554
555 case 1:
556 QuestionValue->Value.time.Minute = (UINT8) EditValue;
557 break;
558
559 case 2:
560 QuestionValue->Value.time.Second = (UINT8) EditValue;
561 break;
562
563 default:
564 break;
565 }
566 } else {
567 //
568 // Numeric
569 //
570 QuestionValue->Value.u64 = EditValue;
571 }
572
573 //
574 // Check to see if the Value is something reasonable against consistency limitations.
575 // If not, let's kick the error specified.
576 //
577 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
578 if (EFI_ERROR (Status)) {
579 //
580 // Input value is not valid, restore Question Value
581 //
582 GetQuestionValue (FormSet, Form, Question, TRUE);
583 } else {
584 SetQuestionValue (FormSet, Form, Question, TRUE);
585 if (!DateOrTime || (Question->Storage != NULL)) {
586 //
587 // NV flag is unnecessary for RTC type of Date/Time
588 //
589 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
590 }
591 }
592
593 return Status;
594 break;
595
596 case CHAR_BACKSPACE:
597 if (ManualInput) {
598 if (Count == 0) {
599 break;
600 }
601 //
602 // Remove a character
603 //
604 EditValue = PreviousNumber[Count - 1];
605 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
606 Count--;
607 Column--;
608 PrintAt (Column, Row, L" ");
609 }
610 break;
611
612 default:
613 if (ManualInput) {
614 if (HexInput) {
615 if (!IsHexDigit (&Digital, Key.UnicodeChar)) {
616 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
617 break;
618 }
619 } else {
620 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
621 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
622 break;
623 }
624 }
625
626 //
627 // If Count exceed input width, there is no way more is valid
628 //
629 if (Count >= InputWidth) {
630 break;
631 }
632 //
633 // Someone typed something valid!
634 //
635 if (Count != 0) {
636 if (HexInput) {
637 EditValue = LShiftU64 (EditValue, 4) + Digital;
638 } else {
639 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
640 }
641 } else {
642 if (HexInput) {
643 EditValue = Digital;
644 } else {
645 EditValue = Key.UnicodeChar - L'0';
646 }
647 }
648
649 if (EditValue > Maximum) {
650 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
651 EditValue = PreviousNumber[Count];
652 break;
653 } else {
654 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
655 }
656
657 Count++;
658 PreviousNumber[Count] = EditValue;
659
660 PrintCharAt (Column, Row, Key.UnicodeChar);
661 Column++;
662 }
663 break;
664 }
665 } while (TRUE);
666
667 }
668
669
670 /**
671 Get selection for OneOf and OrderedList (Left/Right will be ignored).
672
673 @param Selection Pointer to current selection.
674 @param MenuOption Pointer to the current input menu.
675
676 @retval EFI_SUCCESS If Option input is processed successfully
677 @retval EFI_DEVICE_ERROR If operation fails
678
679 **/
680 EFI_STATUS
681 GetSelectionInputPopUp (
682 IN UI_MENU_SELECTION *Selection,
683 IN UI_MENU_OPTION *MenuOption
684 )
685 {
686 EFI_STATUS Status;
687 EFI_INPUT_KEY Key;
688 UINTN Index;
689 CHAR16 *StringPtr;
690 CHAR16 *TempStringPtr;
691 UINTN Index2;
692 UINTN TopOptionIndex;
693 UINTN HighlightOptionIndex;
694 UINTN Start;
695 UINTN End;
696 UINTN Top;
697 UINTN Bottom;
698 UINTN PopUpMenuLines;
699 UINTN MenuLinesInView;
700 UINTN PopUpWidth;
701 CHAR16 Character;
702 INT32 SavedAttribute;
703 BOOLEAN ShowDownArrow;
704 BOOLEAN ShowUpArrow;
705 UINTN DimensionsWidth;
706 LIST_ENTRY *Link;
707 BOOLEAN OrderedList;
708 UINT8 *ValueArray;
709 EFI_HII_VALUE HiiValue;
710 EFI_HII_VALUE *HiiValueArray;
711 UINTN OptionCount;
712 QUESTION_OPTION *OneOfOption;
713 QUESTION_OPTION *CurrentOption;
714 FORM_BROWSER_STATEMENT *Question;
715
716 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
717
718 ValueArray = NULL;
719 CurrentOption = NULL;
720 ShowDownArrow = FALSE;
721 ShowUpArrow = FALSE;
722
723 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
724 ASSERT (StringPtr);
725
726 Question = MenuOption->ThisTag;
727 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
728 ValueArray = Question->BufferValue;
729 OrderedList = TRUE;
730 } else {
731 OrderedList = FALSE;
732 }
733
734 //
735 // Calculate Option count
736 //
737 if (OrderedList) {
738 for (Index = 0; Index < Question->MaxContainers; Index++) {
739 if (ValueArray[Index] == 0) {
740 break;
741 }
742 }
743
744 OptionCount = Index;
745 } else {
746 OptionCount = 0;
747 Link = GetFirstNode (&Question->OptionListHead);
748 while (!IsNull (&Question->OptionListHead, Link)) {
749 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
750
751 OptionCount++;
752
753 Link = GetNextNode (&Question->OptionListHead, Link);
754 }
755 }
756
757 //
758 // Prepare HiiValue array
759 //
760 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
761 ASSERT (HiiValueArray != NULL);
762 Link = GetFirstNode (&Question->OptionListHead);
763 for (Index = 0; Index < OptionCount; Index++) {
764 if (OrderedList) {
765 HiiValueArray[Index].Type = EFI_IFR_TYPE_NUM_SIZE_8;
766 HiiValueArray[Index].Value.u8 = ValueArray[Index];
767 } else {
768 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
769 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
770 Link = GetNextNode (&Question->OptionListHead, Link);
771 }
772 }
773
774 //
775 // Move Suppressed Option to list tail
776 //
777 PopUpMenuLines = 0;
778 for (Index = 0; Index < OptionCount; Index++) {
779 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
780 if (OneOfOption == NULL) {
781 return EFI_NOT_FOUND;
782 }
783
784 RemoveEntryList (&OneOfOption->Link);
785
786 if ((OneOfOption->SuppressExpression != NULL) &&
787 (OneOfOption->SuppressExpression->Result.Value.b)) {
788 //
789 // This option is suppressed, insert to tail
790 //
791 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
792 } else {
793 //
794 // Insert to head
795 //
796 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
797
798 PopUpMenuLines++;
799 }
800 }
801
802 //
803 // Get the number of one of options present and its size
804 //
805 PopUpWidth = 0;
806 HighlightOptionIndex = 0;
807 Link = GetFirstNode (&Question->OptionListHead);
808 for (Index = 0; Index < PopUpMenuLines; Index++) {
809 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
810
811 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
812 if (StrLen (StringPtr) > PopUpWidth) {
813 PopUpWidth = StrLen (StringPtr);
814 }
815 gBS->FreePool (StringPtr);
816
817 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {
818 //
819 // Find current selected Option for OneOf
820 //
821 HighlightOptionIndex = Index;
822 }
823
824 Link = GetNextNode (&Question->OptionListHead, Link);
825 }
826
827 //
828 // Perform popup menu initialization.
829 //
830 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
831
832 SavedAttribute = gST->ConOut->Mode->Attribute;
833 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
834
835 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
836 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
837 }
838
839 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
840 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
841 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
842 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;
843
844 MenuLinesInView = Bottom - Top - 1;
845 if (MenuLinesInView >= PopUpMenuLines) {
846 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
847 Bottom = Top + PopUpMenuLines + 1;
848 } else {
849 ShowDownArrow = TRUE;
850 }
851
852 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
853 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
854 } else {
855 TopOptionIndex = 0;
856 }
857
858 do {
859 //
860 // Clear that portion of the screen
861 //
862 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
863
864 //
865 // Draw "One of" pop-up menu
866 //
867 Character = BOXDRAW_DOWN_RIGHT;
868 PrintCharAt (Start, Top, Character);
869 for (Index = Start; Index + 2 < End; Index++) {
870 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
871 Character = GEOMETRICSHAPE_UP_TRIANGLE;
872 } else {
873 Character = BOXDRAW_HORIZONTAL;
874 }
875
876 PrintChar (Character);
877 }
878
879 Character = BOXDRAW_DOWN_LEFT;
880 PrintChar (Character);
881 Character = BOXDRAW_VERTICAL;
882 for (Index = Top + 1; Index < Bottom; Index++) {
883 PrintCharAt (Start, Index, Character);
884 PrintCharAt (End - 1, Index, Character);
885 }
886
887 //
888 // Move to top Option
889 //
890 Link = GetFirstNode (&Question->OptionListHead);
891 for (Index = 0; Index < TopOptionIndex; Index++) {
892 Link = GetNextNode (&Question->OptionListHead, Link);
893 }
894
895 //
896 // Display the One of options
897 //
898 Index2 = Top + 1;
899 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
900 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
901 Link = GetNextNode (&Question->OptionListHead, Link);
902
903 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
904 //
905 // If the string occupies multiple lines, truncate it to fit in one line,
906 // and append a "..." for indication.
907 //
908 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
909 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
910 ASSERT ( TempStringPtr != NULL );
911 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
912 gBS->FreePool (StringPtr);
913 StringPtr = TempStringPtr;
914 StrCat (StringPtr, L"...");
915 }
916
917 if (Index == HighlightOptionIndex) {
918 //
919 // Highlight the selected one
920 //
921 CurrentOption = OneOfOption;
922
923 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
924 PrintStringAt (Start + 2, Index2, StringPtr);
925 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
926 } else {
927 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
928 PrintStringAt (Start + 2, Index2, StringPtr);
929 }
930
931 Index2++;
932 gBS->FreePool (StringPtr);
933 }
934
935 Character = BOXDRAW_UP_RIGHT;
936 PrintCharAt (Start, Bottom, Character);
937 for (Index = Start; Index + 2 < End; Index++) {
938 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
939 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
940 } else {
941 Character = BOXDRAW_HORIZONTAL;
942 }
943
944 PrintChar (Character);
945 }
946
947 Character = BOXDRAW_UP_LEFT;
948 PrintChar (Character);
949
950 //
951 // Get User selection
952 //
953 Key.UnicodeChar = CHAR_NULL;
954 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
955 Key.ScanCode = gDirection;
956 gDirection = 0;
957 goto TheKey;
958 }
959
960 Status = WaitForKeyStroke (&Key);
961
962 TheKey:
963 switch (Key.UnicodeChar) {
964 case '+':
965 if (OrderedList) {
966 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
967 //
968 // Highlight reaches the top of the popup window, scroll one menu item.
969 //
970 TopOptionIndex--;
971 ShowDownArrow = TRUE;
972 }
973
974 if (TopOptionIndex == 0) {
975 ShowUpArrow = FALSE;
976 }
977
978 if (HighlightOptionIndex > 0) {
979 HighlightOptionIndex--;
980
981 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
982 }
983 }
984 break;
985
986 case '-':
987 //
988 // If an ordered list op-code, we will allow for a popup of +/- keys
989 // to create an ordered list of items
990 //
991 if (OrderedList) {
992 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
993 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
994 //
995 // Highlight reaches the bottom of the popup window, scroll one menu item.
996 //
997 TopOptionIndex++;
998 ShowUpArrow = TRUE;
999 }
1000
1001 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1002 ShowDownArrow = FALSE;
1003 }
1004
1005 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1006 HighlightOptionIndex++;
1007
1008 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1009 }
1010 }
1011 break;
1012
1013 case CHAR_NULL:
1014 switch (Key.ScanCode) {
1015 case SCAN_UP:
1016 case SCAN_DOWN:
1017 if (Key.ScanCode == SCAN_UP) {
1018 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1019 //
1020 // Highlight reaches the top of the popup window, scroll one menu item.
1021 //
1022 TopOptionIndex--;
1023 ShowDownArrow = TRUE;
1024 }
1025
1026 if (TopOptionIndex == 0) {
1027 ShowUpArrow = FALSE;
1028 }
1029
1030 if (HighlightOptionIndex > 0) {
1031 HighlightOptionIndex--;
1032 }
1033 } else {
1034 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1035 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1036 //
1037 // Highlight reaches the bottom of the popup window, scroll one menu item.
1038 //
1039 TopOptionIndex++;
1040 ShowUpArrow = TRUE;
1041 }
1042
1043 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1044 ShowDownArrow = FALSE;
1045 }
1046
1047 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1048 HighlightOptionIndex++;
1049 }
1050 }
1051 break;
1052
1053 case SCAN_ESC:
1054 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1055
1056 //
1057 // Restore link list order for orderedlist
1058 //
1059 if (OrderedList) {
1060 HiiValue.Type = EFI_IFR_TYPE_NUM_SIZE_8;
1061 HiiValue.Value.u64 = 0;
1062 for (Index = 0; Index < Question->MaxContainers; Index++) {
1063 HiiValue.Value.u8 = ValueArray[Index];
1064 if (HiiValue.Value.u8) {
1065 break;
1066 }
1067
1068 OneOfOption = ValueToOption (Question, &HiiValue);
1069 if (OneOfOption == NULL) {
1070 return EFI_NOT_FOUND;
1071 }
1072
1073 RemoveEntryList (&OneOfOption->Link);
1074 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1075 }
1076 }
1077
1078 gBS->FreePool (HiiValueArray);
1079 return EFI_DEVICE_ERROR;
1080
1081 default:
1082 break;
1083 }
1084
1085 break;
1086
1087 case CHAR_CARRIAGE_RETURN:
1088 //
1089 // return the current selection
1090 //
1091 if (OrderedList) {
1092 Index = 0;
1093 Link = GetFirstNode (&Question->OptionListHead);
1094 while (!IsNull (&Question->OptionListHead, Link)) {
1095 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1096
1097 Question->BufferValue[Index] = OneOfOption->Value.Value.u8;
1098
1099 Index++;
1100 if (Index > Question->MaxContainers) {
1101 break;
1102 }
1103
1104 Link = GetNextNode (&Question->OptionListHead, Link);
1105 }
1106 } else {
1107 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
1108 }
1109
1110 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1111 gBS->FreePool (HiiValueArray);
1112
1113 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
1114 if (EFI_ERROR (Status)) {
1115 //
1116 // Input value is not valid, restore Question Value
1117 //
1118 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1119 } else {
1120 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1121 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
1122 }
1123
1124 return Status;
1125
1126 default:
1127 break;
1128 }
1129 } while (TRUE);
1130
1131 }
1132
1133 EFI_STATUS
1134 WaitForKeyStroke (
1135 OUT EFI_INPUT_KEY *Key
1136 )
1137 {
1138 EFI_STATUS Status;
1139
1140 do {
1141 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
1142 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1143 } while (EFI_ERROR(Status));
1144
1145 return Status;
1146 }