]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
1) Add BufToHexString, HexStringToBuf and IsHexDigit to BaseLib.
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / InputHandler.c
CommitLineData
93e3992d 1/** @file
2
3Copyright (c) 2004 - 2007, Intel Corporation
4All rights reserved. This program and the accompanying materials
5are licensed and made available under the terms and conditions of the BSD License
6which accompanies this distribution. The full text of the license may be found at
7http://opensource.org/licenses/bsd-license.php
8
9THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
10WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
11
12Module Name:
13
14 InputHandler.c
15
16Abstract:
17
18 Implementation for handling user input from the User Interface
19
20Revision 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**/
40EFI_STATUS
41ReadString (
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**/
239EFI_STATUS
240GetNumericInput (
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
421TheKey2:
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
526EnterCarriageReturn:
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) {
36fe40c2 615 if (!IsHexDigit (&Digital, Key.UnicodeChar)) {
93e3992d 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**/
680EFI_STATUS
681GetSelectionInputPopUp (
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
962TheKey:
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
1133EFI_STATUS
1134WaitForKeyStroke (
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}