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