]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
Add support for newly defined VarStore type EFI_IFR_TYPE_UNDEFINED, EFI_IFR_TYPE_ACTI...
[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 - 2009, 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 CreateMultiStringPopUp (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 FreePool (TempString);
118 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 FreePool (TempString);
133 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 FreePool (TempString);
147 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[MAX_NUMERIC_INPUT_WIDTH];
239 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
240 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
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 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
391 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
392 InputText[InputWidth + 2] = L'\0';
393
394 PrintAt (Column, Row, InputText);
395 Column++;
396 }
397
398 //
399 // First time we enter this handler, we need to check to see if
400 // we were passed an increment or decrement directive
401 //
402 do {
403 Key.UnicodeChar = CHAR_NULL;
404 if (gDirection != 0) {
405 Key.ScanCode = gDirection;
406 gDirection = 0;
407 goto TheKey2;
408 }
409
410 Status = WaitForKeyStroke (&Key);
411
412 TheKey2:
413 switch (Key.UnicodeChar) {
414
415 case '+':
416 case '-':
417 if (Key.UnicodeChar == '+') {
418 Key.ScanCode = SCAN_RIGHT;
419 } else {
420 Key.ScanCode = SCAN_LEFT;
421 }
422 Key.UnicodeChar = CHAR_NULL;
423 goto TheKey2;
424
425 case CHAR_NULL:
426 switch (Key.ScanCode) {
427 case SCAN_LEFT:
428 case SCAN_RIGHT:
429 if (DateOrTime) {
430 //
431 // By setting this value, we will return back to the caller.
432 // We need to do this since an auto-refresh will destroy the adjustment
433 // based on what the real-time-clock is showing. So we always commit
434 // upon changing the value.
435 //
436 gDirection = SCAN_DOWN;
437 }
438
439 if (!ManualInput) {
440 if (Key.ScanCode == SCAN_LEFT) {
441 if (EditValue > Step) {
442 EditValue = EditValue - Step;
443 } else {
444 EditValue = Minimum;
445 }
446 } else if (Key.ScanCode == SCAN_RIGHT) {
447 EditValue = EditValue + Step;
448 if (EditValue > Maximum) {
449 EditValue = Maximum;
450 }
451 }
452
453 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
454 if (Question->Operand == EFI_IFR_DATE_OP) {
455 if (MenuOption->Sequence == 2) {
456 //
457 // Year
458 //
459 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", EditValue);
460 } else {
461 //
462 // Month/Day
463 //
464 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
465 }
466
467 if (MenuOption->Sequence == 0) {
468 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
469 } else if (MenuOption->Sequence == 1) {
470 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
471 }
472 } else if (Question->Operand == EFI_IFR_TIME_OP) {
473 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);
474
475 if (MenuOption->Sequence == 0) {
476 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
477 } else if (MenuOption->Sequence == 1) {
478 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
479 }
480 } else {
481 QuestionValue->Value.u64 = EditValue;
482 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
483 }
484
485 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
486 for (Loop = 0; Loop < EraseLen; Loop++) {
487 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
488 }
489 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
490
491 if (MenuOption->Sequence == 0) {
492 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
493 Column = MenuOption->OptCol + 1;
494 }
495
496 PrintStringAt (Column, Row, FormattedNumber);
497
498 if (!DateOrTime || MenuOption->Sequence == 2) {
499 PrintChar (RIGHT_NUMERIC_DELIMITER);
500 }
501 }
502
503 goto EnterCarriageReturn;
504 break;
505
506 case SCAN_UP:
507 case SCAN_DOWN:
508 goto EnterCarriageReturn;
509
510 case SCAN_ESC:
511 return EFI_DEVICE_ERROR;
512
513 default:
514 break;
515 }
516
517 break;
518
519 EnterCarriageReturn:
520
521 case CHAR_CARRIAGE_RETURN:
522 //
523 // Store Edit value back to Question
524 //
525 if (Question->Operand == EFI_IFR_DATE_OP) {
526 switch (MenuOption->Sequence) {
527 case 0:
528 QuestionValue->Value.date.Month = (UINT8) EditValue;
529 break;
530
531 case 1:
532 QuestionValue->Value.date.Day = (UINT8) EditValue;
533 break;
534
535 case 2:
536 QuestionValue->Value.date.Year = (UINT16) EditValue;
537 break;
538
539 default:
540 break;
541 }
542 } else if (Question->Operand == EFI_IFR_TIME_OP) {
543 switch (MenuOption->Sequence) {
544 case 0:
545 QuestionValue->Value.time.Hour = (UINT8) EditValue;
546 break;
547
548 case 1:
549 QuestionValue->Value.time.Minute = (UINT8) EditValue;
550 break;
551
552 case 2:
553 QuestionValue->Value.time.Second = (UINT8) EditValue;
554 break;
555
556 default:
557 break;
558 }
559 } else {
560 //
561 // Numeric
562 //
563 QuestionValue->Value.u64 = EditValue;
564 }
565
566 //
567 // Check to see if the Value is something reasonable against consistency limitations.
568 // If not, let's kick the error specified.
569 //
570 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
571 if (EFI_ERROR (Status)) {
572 //
573 // Input value is not valid, restore Question Value
574 //
575 GetQuestionValue (FormSet, Form, Question, TRUE);
576 } else {
577 SetQuestionValue (FormSet, Form, Question, TRUE);
578 if (!DateOrTime || (Question->Storage != NULL)) {
579 //
580 // NV flag is unnecessary for RTC type of Date/Time
581 //
582 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
583 }
584 }
585
586 return Status;
587 break;
588
589 case CHAR_BACKSPACE:
590 if (ManualInput) {
591 if (Count == 0) {
592 break;
593 }
594 //
595 // Remove a character
596 //
597 EditValue = PreviousNumber[Count - 1];
598 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
599 Count--;
600 Column--;
601 PrintAt (Column, Row, L" ");
602 }
603 break;
604
605 default:
606 if (ManualInput) {
607 if (HexInput) {
608 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
609 Digital = (UINT8) (Key.UnicodeChar - L'0');
610 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
611 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
612 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
613 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
614 } else {
615 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
616 break;
617 }
618 } else {
619 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
620 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
621 break;
622 }
623 }
624
625 //
626 // If Count exceed input width, there is no way more is valid
627 //
628 if (Count >= InputWidth) {
629 break;
630 }
631 //
632 // Someone typed something valid!
633 //
634 if (Count != 0) {
635 if (HexInput) {
636 EditValue = LShiftU64 (EditValue, 4) + Digital;
637 } else {
638 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
639 }
640 } else {
641 if (HexInput) {
642 EditValue = Digital;
643 } else {
644 EditValue = Key.UnicodeChar - L'0';
645 }
646 }
647
648 if (EditValue > Maximum) {
649 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);
650 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
651 EditValue = PreviousNumber[Count];
652 break;
653 } else {
654 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);
655 }
656
657 Count++;
658 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
659 PreviousNumber[Count] = EditValue;
660
661 PrintCharAt (Column, Row, Key.UnicodeChar);
662 Column++;
663 }
664 break;
665 }
666 } while (TRUE);
667
668 }
669
670
671 /**
672 Get selection for OneOf and OrderedList (Left/Right will be ignored).
673
674 @param Selection Pointer to current selection.
675 @param MenuOption Pointer to the current input menu.
676
677 @retval EFI_SUCCESS If Option input is processed successfully
678 @retval EFI_DEVICE_ERROR If operation fails
679
680 **/
681 EFI_STATUS
682 GetSelectionInputPopUp (
683 IN UI_MENU_SELECTION *Selection,
684 IN UI_MENU_OPTION *MenuOption
685 )
686 {
687 EFI_STATUS Status;
688 EFI_INPUT_KEY Key;
689 UINTN Index;
690 CHAR16 *StringPtr;
691 CHAR16 *TempStringPtr;
692 UINTN Index2;
693 UINTN TopOptionIndex;
694 UINTN HighlightOptionIndex;
695 UINTN Start;
696 UINTN End;
697 UINTN Top;
698 UINTN Bottom;
699 UINTN PopUpMenuLines;
700 UINTN MenuLinesInView;
701 UINTN PopUpWidth;
702 CHAR16 Character;
703 INT32 SavedAttribute;
704 BOOLEAN ShowDownArrow;
705 BOOLEAN ShowUpArrow;
706 UINTN DimensionsWidth;
707 LIST_ENTRY *Link;
708 BOOLEAN OrderedList;
709 UINT8 *ValueArray;
710 UINT8 ValueType;
711 EFI_HII_VALUE HiiValue;
712 EFI_HII_VALUE *HiiValueArray;
713 UINTN OptionCount;
714 QUESTION_OPTION *OneOfOption;
715 QUESTION_OPTION *CurrentOption;
716 FORM_BROWSER_STATEMENT *Question;
717
718 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
719
720 ValueArray = NULL;
721 ValueType = 0;
722 CurrentOption = NULL;
723 ShowDownArrow = FALSE;
724 ShowUpArrow = FALSE;
725
726 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
727 ASSERT (StringPtr);
728
729 Question = MenuOption->ThisTag;
730 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
731 ValueArray = Question->BufferValue;
732 OrderedList = TRUE;
733 } else {
734 OrderedList = FALSE;
735 }
736
737 //
738 // Calculate Option count
739 //
740 if (OrderedList) {
741 for (Index = 0; Index < Question->MaxContainers; Index++) {
742 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
743 break;
744 }
745 }
746
747 OptionCount = Index;
748 } else {
749 OptionCount = 0;
750 Link = GetFirstNode (&Question->OptionListHead);
751 while (!IsNull (&Question->OptionListHead, Link)) {
752 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
753
754 OptionCount++;
755
756 Link = GetNextNode (&Question->OptionListHead, Link);
757 }
758 }
759
760 //
761 // Prepare HiiValue array
762 //
763 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
764 ASSERT (HiiValueArray != NULL);
765 Link = GetFirstNode (&Question->OptionListHead);
766 for (Index = 0; Index < OptionCount; Index++) {
767 if (OrderedList) {
768 HiiValueArray[Index].Type = ValueType;
769 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
770 } else {
771 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
772 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
773 Link = GetNextNode (&Question->OptionListHead, Link);
774 }
775 }
776
777 //
778 // Move Suppressed Option to list tail
779 //
780 PopUpMenuLines = 0;
781 for (Index = 0; Index < OptionCount; Index++) {
782 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
783 if (OneOfOption == NULL) {
784 return EFI_NOT_FOUND;
785 }
786
787 RemoveEntryList (&OneOfOption->Link);
788
789 if ((OneOfOption->SuppressExpression != NULL) &&
790 (OneOfOption->SuppressExpression->Result.Value.b)) {
791 //
792 // This option is suppressed, insert to tail
793 //
794 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
795 } else {
796 //
797 // Insert to head
798 //
799 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
800
801 PopUpMenuLines++;
802 }
803 }
804
805 //
806 // Get the number of one of options present and its size
807 //
808 PopUpWidth = 0;
809 HighlightOptionIndex = 0;
810 Link = GetFirstNode (&Question->OptionListHead);
811 for (Index = 0; Index < PopUpMenuLines; Index++) {
812 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
813
814 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
815 if (StrLen (StringPtr) > PopUpWidth) {
816 PopUpWidth = StrLen (StringPtr);
817 }
818 FreePool (StringPtr);
819
820 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {
821 //
822 // Find current selected Option for OneOf
823 //
824 HighlightOptionIndex = Index;
825 }
826
827 Link = GetNextNode (&Question->OptionListHead, Link);
828 }
829
830 //
831 // Perform popup menu initialization.
832 //
833 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
834
835 SavedAttribute = gST->ConOut->Mode->Attribute;
836 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
837
838 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
839 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
840 }
841
842 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
843 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
844 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
845 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;
846
847 MenuLinesInView = Bottom - Top - 1;
848 if (MenuLinesInView >= PopUpMenuLines) {
849 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
850 Bottom = Top + PopUpMenuLines + 1;
851 } else {
852 ShowDownArrow = TRUE;
853 }
854
855 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
856 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
857 } else {
858 TopOptionIndex = 0;
859 }
860
861 do {
862 //
863 // Clear that portion of the screen
864 //
865 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
866
867 //
868 // Draw "One of" pop-up menu
869 //
870 Character = BOXDRAW_DOWN_RIGHT;
871 PrintCharAt (Start, Top, Character);
872 for (Index = Start; Index + 2 < End; Index++) {
873 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
874 Character = GEOMETRICSHAPE_UP_TRIANGLE;
875 } else {
876 Character = BOXDRAW_HORIZONTAL;
877 }
878
879 PrintChar (Character);
880 }
881
882 Character = BOXDRAW_DOWN_LEFT;
883 PrintChar (Character);
884 Character = BOXDRAW_VERTICAL;
885 for (Index = Top + 1; Index < Bottom; Index++) {
886 PrintCharAt (Start, Index, Character);
887 PrintCharAt (End - 1, Index, Character);
888 }
889
890 //
891 // Move to top Option
892 //
893 Link = GetFirstNode (&Question->OptionListHead);
894 for (Index = 0; Index < TopOptionIndex; Index++) {
895 Link = GetNextNode (&Question->OptionListHead, Link);
896 }
897
898 //
899 // Display the One of options
900 //
901 Index2 = Top + 1;
902 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
903 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
904 Link = GetNextNode (&Question->OptionListHead, Link);
905
906 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
907 //
908 // If the string occupies multiple lines, truncate it to fit in one line,
909 // and append a "..." for indication.
910 //
911 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
912 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
913 ASSERT ( TempStringPtr != NULL );
914 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
915 FreePool (StringPtr);
916 StringPtr = TempStringPtr;
917 StrCat (StringPtr, L"...");
918 }
919
920 if (Index == HighlightOptionIndex) {
921 //
922 // Highlight the selected one
923 //
924 CurrentOption = OneOfOption;
925
926 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
927 PrintStringAt (Start + 2, Index2, StringPtr);
928 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
929 } else {
930 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
931 PrintStringAt (Start + 2, Index2, StringPtr);
932 }
933
934 Index2++;
935 FreePool (StringPtr);
936 }
937
938 Character = BOXDRAW_UP_RIGHT;
939 PrintCharAt (Start, Bottom, Character);
940 for (Index = Start; Index + 2 < End; Index++) {
941 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
942 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
943 } else {
944 Character = BOXDRAW_HORIZONTAL;
945 }
946
947 PrintChar (Character);
948 }
949
950 Character = BOXDRAW_UP_LEFT;
951 PrintChar (Character);
952
953 //
954 // Get User selection
955 //
956 Key.UnicodeChar = CHAR_NULL;
957 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
958 Key.ScanCode = gDirection;
959 gDirection = 0;
960 goto TheKey;
961 }
962
963 Status = WaitForKeyStroke (&Key);
964
965 TheKey:
966 switch (Key.UnicodeChar) {
967 case '+':
968 if (OrderedList) {
969 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
970 //
971 // Highlight reaches the top of the popup window, scroll one menu item.
972 //
973 TopOptionIndex--;
974 ShowDownArrow = TRUE;
975 }
976
977 if (TopOptionIndex == 0) {
978 ShowUpArrow = FALSE;
979 }
980
981 if (HighlightOptionIndex > 0) {
982 HighlightOptionIndex--;
983
984 ASSERT (CurrentOption != NULL);
985 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
986 }
987 }
988 break;
989
990 case '-':
991 //
992 // If an ordered list op-code, we will allow for a popup of +/- keys
993 // to create an ordered list of items
994 //
995 if (OrderedList) {
996 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
997 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
998 //
999 // Highlight reaches the bottom of the popup window, scroll one menu item.
1000 //
1001 TopOptionIndex++;
1002 ShowUpArrow = TRUE;
1003 }
1004
1005 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1006 ShowDownArrow = FALSE;
1007 }
1008
1009 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1010 HighlightOptionIndex++;
1011
1012 ASSERT (CurrentOption != NULL);
1013 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1014 }
1015 }
1016 break;
1017
1018 case CHAR_NULL:
1019 switch (Key.ScanCode) {
1020 case SCAN_UP:
1021 case SCAN_DOWN:
1022 if (Key.ScanCode == SCAN_UP) {
1023 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1024 //
1025 // Highlight reaches the top of the popup window, scroll one menu item.
1026 //
1027 TopOptionIndex--;
1028 ShowDownArrow = TRUE;
1029 }
1030
1031 if (TopOptionIndex == 0) {
1032 ShowUpArrow = FALSE;
1033 }
1034
1035 if (HighlightOptionIndex > 0) {
1036 HighlightOptionIndex--;
1037 }
1038 } else {
1039 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1040 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1041 //
1042 // Highlight reaches the bottom of the popup window, scroll one menu item.
1043 //
1044 TopOptionIndex++;
1045 ShowUpArrow = TRUE;
1046 }
1047
1048 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1049 ShowDownArrow = FALSE;
1050 }
1051
1052 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1053 HighlightOptionIndex++;
1054 }
1055 }
1056 break;
1057
1058 case SCAN_ESC:
1059 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1060
1061 //
1062 // Restore link list order for orderedlist
1063 //
1064 if (OrderedList) {
1065 HiiValue.Type = ValueType;
1066 HiiValue.Value.u64 = 0;
1067 for (Index = 0; Index < Question->MaxContainers; Index++) {
1068 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1069 if (HiiValue.Value.u64 == 0) {
1070 break;
1071 }
1072
1073 OneOfOption = ValueToOption (Question, &HiiValue);
1074 if (OneOfOption == NULL) {
1075 return EFI_NOT_FOUND;
1076 }
1077
1078 RemoveEntryList (&OneOfOption->Link);
1079 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1080 }
1081 }
1082
1083 FreePool (HiiValueArray);
1084 return EFI_DEVICE_ERROR;
1085
1086 default:
1087 break;
1088 }
1089
1090 break;
1091
1092 case CHAR_CARRIAGE_RETURN:
1093 //
1094 // return the current selection
1095 //
1096 if (OrderedList) {
1097 Index = 0;
1098 Link = GetFirstNode (&Question->OptionListHead);
1099 while (!IsNull (&Question->OptionListHead, Link)) {
1100 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1101
1102 SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);
1103
1104 Index++;
1105 if (Index > Question->MaxContainers) {
1106 break;
1107 }
1108
1109 Link = GetNextNode (&Question->OptionListHead, Link);
1110 }
1111 } else {
1112 ASSERT (CurrentOption != NULL);
1113 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
1114 }
1115
1116 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1117 FreePool (HiiValueArray);
1118
1119 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
1120 if (EFI_ERROR (Status)) {
1121 //
1122 // Input value is not valid, restore Question Value
1123 //
1124 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1125 } else {
1126 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1127 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
1128 }
1129
1130 return Status;
1131
1132 default:
1133 break;
1134 }
1135 } while (TRUE);
1136
1137 }
1138
1139 /**
1140 Wait for a key to be pressed by user.
1141
1142 @param Key The key which is pressed by user.
1143
1144 @retval EFI_SUCCESS The function always completed successfully.
1145
1146 **/
1147 EFI_STATUS
1148 WaitForKeyStroke (
1149 OUT EFI_INPUT_KEY *Key
1150 )
1151 {
1152 EFI_STATUS Status;
1153
1154 do {
1155 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
1156 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1157 } while (EFI_ERROR(Status));
1158
1159 return Status;
1160 }