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