]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
Adjust the day field when update the month and year field.
[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 - 2011, 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 Adjust the value to the correct one. Rules follow the sample:
219 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
220 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
221
222 @param Question Pointer to current question.
223 @param Sequence The sequence of the field in the question.
224 **/
225 VOID
226 AdjustQuestionValue (
227 IN FORM_BROWSER_STATEMENT *Question,
228 IN UINT8 Sequence
229 )
230 {
231 UINT8 Month;
232 UINT16 Year;
233 UINT8 Maximum;
234 UINT8 Minimum;
235
236 if (Question->Operand != EFI_IFR_DATE_OP) {
237 return;
238 }
239
240 Month = Question->HiiValue.Value.date.Month;
241 Year = Question->HiiValue.Value.date.Year;
242 Minimum = 1;
243
244 switch (Month) {
245 case 2:
246 if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {
247 Maximum = 29;
248 } else {
249 Maximum = 28;
250 }
251 break;
252 case 4:
253 case 6:
254 case 9:
255 case 11:
256 Maximum = 30;
257 break;
258 default:
259 Maximum = 31;
260 break;
261 }
262
263 //
264 // Change the month area.
265 //
266 if (Sequence == 0) {
267 if (Question->HiiValue.Value.date.Day > Maximum) {
268 Question->HiiValue.Value.date.Day = Maximum;
269 }
270 }
271
272 //
273 // Change the Year area.
274 //
275 if (Sequence == 2) {
276 if (Question->HiiValue.Value.date.Day > Maximum) {
277 Question->HiiValue.Value.date.Day = Minimum;
278 }
279 }
280 }
281
282 /**
283 This routine reads a numeric value from the user input.
284
285 @param Selection Pointer to current selection.
286 @param MenuOption Pointer to the current input menu.
287
288 @retval EFI_SUCCESS If numerical input is read successfully
289 @retval EFI_DEVICE_ERROR If operation fails
290
291 **/
292 EFI_STATUS
293 GetNumericInput (
294 IN UI_MENU_SELECTION *Selection,
295 IN UI_MENU_OPTION *MenuOption
296 )
297 {
298 EFI_STATUS Status;
299 UINTN Column;
300 UINTN Row;
301 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
302 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
303 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
304 UINTN Count;
305 UINTN Loop;
306 BOOLEAN ManualInput;
307 BOOLEAN HexInput;
308 BOOLEAN DateOrTime;
309 UINTN InputWidth;
310 UINT64 EditValue;
311 UINT64 Step;
312 UINT64 Minimum;
313 UINT64 Maximum;
314 UINTN EraseLen;
315 UINT8 Digital;
316 EFI_INPUT_KEY Key;
317 EFI_HII_VALUE *QuestionValue;
318 FORM_BROWSER_FORM *Form;
319 FORM_BROWSER_FORMSET *FormSet;
320 FORM_BROWSER_STATEMENT *Question;
321
322 Column = MenuOption->OptCol;
323 Row = MenuOption->Row;
324 PreviousNumber[0] = 0;
325 Count = 0;
326 InputWidth = 0;
327 Digital = 0;
328
329 FormSet = Selection->FormSet;
330 Form = Selection->Form;
331 Question = MenuOption->ThisTag;
332 QuestionValue = &Question->HiiValue;
333 Step = Question->Step;
334 Minimum = Question->Minimum;
335 Maximum = Question->Maximum;
336
337 //
338 // Only two case, user can enter to this function: Enter and +/- case.
339 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
340 //
341 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
342
343 if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {
344 DateOrTime = TRUE;
345 } else {
346 DateOrTime = FALSE;
347 }
348
349 //
350 // Prepare Value to be edit
351 //
352 EraseLen = 0;
353 EditValue = 0;
354 if (Question->Operand == EFI_IFR_DATE_OP) {
355 Step = 1;
356 Minimum = 1;
357
358 switch (MenuOption->Sequence) {
359 case 0:
360 Maximum = 12;
361 EraseLen = 4;
362 EditValue = QuestionValue->Value.date.Month;
363 break;
364
365 case 1:
366 switch (QuestionValue->Value.date.Month) {
367 case 2:
368 if ((QuestionValue->Value.date.Year % 4) == 0 &&
369 ((QuestionValue->Value.date.Year % 100) != 0 ||
370 (QuestionValue->Value.date.Year % 400) == 0)) {
371 Maximum = 29;
372 } else {
373 Maximum = 28;
374 }
375 break;
376 case 4:
377 case 6:
378 case 9:
379 case 11:
380 Maximum = 30;
381 break;
382 default:
383 Maximum = 31;
384 break;
385 }
386
387 EraseLen = 3;
388 EditValue = QuestionValue->Value.date.Day;
389 break;
390
391 case 2:
392 Maximum = 0xffff;
393 EraseLen = 5;
394 EditValue = QuestionValue->Value.date.Year;
395 break;
396
397 default:
398 break;
399 }
400 } else if (Question->Operand == EFI_IFR_TIME_OP) {
401 Step = 1;
402 Minimum = 0;
403
404 switch (MenuOption->Sequence) {
405 case 0:
406 Maximum = 23;
407 EraseLen = 4;
408 EditValue = QuestionValue->Value.time.Hour;
409 break;
410
411 case 1:
412 Maximum = 59;
413 EraseLen = 3;
414 EditValue = QuestionValue->Value.time.Minute;
415 break;
416
417 case 2:
418 Maximum = 59;
419 EraseLen = 3;
420 EditValue = QuestionValue->Value.time.Second;
421 break;
422
423 default:
424 break;
425 }
426 } else {
427 //
428 // Numeric
429 //
430 EraseLen = gOptionBlockWidth;
431 EditValue = QuestionValue->Value.u64;
432 if (Maximum == 0) {
433 Maximum = (UINT64) -1;
434 }
435 }
436
437 if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&
438 ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
439 HexInput = TRUE;
440 } else {
441 HexInput = FALSE;
442 }
443
444 //
445 // Enter from "Enter" input, clear the old word showing.
446 //
447 if (ManualInput) {
448 if (Question->Operand == EFI_IFR_NUMERIC_OP) {
449 if (HexInput) {
450 InputWidth = Question->StorageWidth * 2;
451 } else {
452 switch (Question->StorageWidth) {
453 case 1:
454 InputWidth = 3;
455 break;
456
457 case 2:
458 InputWidth = 5;
459 break;
460
461 case 4:
462 InputWidth = 10;
463 break;
464
465 case 8:
466 InputWidth = 20;
467 break;
468
469 default:
470 InputWidth = 0;
471 break;
472 }
473 }
474
475 InputText[0] = LEFT_NUMERIC_DELIMITER;
476 SetUnicodeMem (InputText + 1, InputWidth, L' ');
477 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
478 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
479 InputText[InputWidth + 2] = L'\0';
480
481 PrintAt (Column, Row, InputText);
482 Column++;
483 }
484
485 if (Question->Operand == EFI_IFR_DATE_OP) {
486 if (MenuOption->Sequence == 2) {
487 InputWidth = 4;
488 } else {
489 InputWidth = 2;
490 }
491
492 if (MenuOption->Sequence == 0) {
493 InputText[0] = LEFT_NUMERIC_DELIMITER;
494 SetUnicodeMem (InputText + 1, InputWidth, L' ');
495 } else {
496 SetUnicodeMem (InputText, InputWidth, L' ');
497 }
498
499 if (MenuOption->Sequence == 2) {
500 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
501 } else {
502 InputText[InputWidth + 1] = DATE_SEPARATOR;
503 }
504 InputText[InputWidth + 2] = L'\0';
505
506 PrintAt (Column, Row, InputText);
507 if (MenuOption->Sequence == 0) {
508 Column++;
509 }
510 }
511
512 if (Question->Operand == EFI_IFR_TIME_OP) {
513 InputWidth = 2;
514
515 if (MenuOption->Sequence == 0) {
516 InputText[0] = LEFT_NUMERIC_DELIMITER;
517 SetUnicodeMem (InputText + 1, InputWidth, L' ');
518 } else {
519 SetUnicodeMem (InputText, InputWidth, L' ');
520 }
521
522 if (MenuOption->Sequence == 2) {
523 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
524 } else {
525 InputText[InputWidth + 1] = TIME_SEPARATOR;
526 }
527 InputText[InputWidth + 2] = L'\0';
528
529 PrintAt (Column, Row, InputText);
530 if (MenuOption->Sequence == 0) {
531 Column++;
532 }
533 }
534 }
535
536 //
537 // First time we enter this handler, we need to check to see if
538 // we were passed an increment or decrement directive
539 //
540 do {
541 Key.UnicodeChar = CHAR_NULL;
542 if (gDirection != 0) {
543 Key.ScanCode = gDirection;
544 gDirection = 0;
545 goto TheKey2;
546 }
547
548 Status = WaitForKeyStroke (&Key);
549
550 TheKey2:
551 switch (Key.UnicodeChar) {
552
553 case '+':
554 case '-':
555 if (Key.UnicodeChar == '+') {
556 Key.ScanCode = SCAN_RIGHT;
557 } else {
558 Key.ScanCode = SCAN_LEFT;
559 }
560 Key.UnicodeChar = CHAR_NULL;
561 goto TheKey2;
562
563 case CHAR_NULL:
564 switch (Key.ScanCode) {
565 case SCAN_LEFT:
566 case SCAN_RIGHT:
567 if (DateOrTime && !ManualInput) {
568 //
569 // By setting this value, we will return back to the caller.
570 // We need to do this since an auto-refresh will destroy the adjustment
571 // based on what the real-time-clock is showing. So we always commit
572 // upon changing the value.
573 //
574 gDirection = SCAN_DOWN;
575 }
576
577 if ((Step != 0) && !ManualInput) {
578 if (Key.ScanCode == SCAN_LEFT) {
579 if (EditValue >= Minimum + Step) {
580 EditValue = EditValue - Step;
581 } else if (EditValue > Minimum){
582 EditValue = Minimum;
583 } else {
584 EditValue = Maximum;
585 }
586 } else if (Key.ScanCode == SCAN_RIGHT) {
587 if (EditValue + Step <= Maximum) {
588 EditValue = EditValue + Step;
589 } else if (EditValue < Maximum) {
590 EditValue = Maximum;
591 } else {
592 EditValue = Minimum;
593 }
594 }
595
596 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
597 if (Question->Operand == EFI_IFR_DATE_OP) {
598 if (MenuOption->Sequence == 2) {
599 //
600 // Year
601 //
602 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
603 } else {
604 //
605 // Month/Day
606 //
607 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
608 }
609
610 if (MenuOption->Sequence == 0) {
611 ASSERT (EraseLen >= 2);
612 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
613 } else if (MenuOption->Sequence == 1) {
614 ASSERT (EraseLen >= 1);
615 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
616 }
617 } else if (Question->Operand == EFI_IFR_TIME_OP) {
618 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
619
620 if (MenuOption->Sequence == 0) {
621 ASSERT (EraseLen >= 2);
622 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
623 } else if (MenuOption->Sequence == 1) {
624 ASSERT (EraseLen >= 1);
625 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
626 }
627 } else {
628 QuestionValue->Value.u64 = EditValue;
629 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
630 }
631
632 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
633 for (Loop = 0; Loop < EraseLen; Loop++) {
634 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
635 }
636 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));
637
638 if (MenuOption->Sequence == 0) {
639 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
640 Column = MenuOption->OptCol + 1;
641 }
642
643 PrintStringAt (Column, Row, FormattedNumber);
644
645 if (!DateOrTime || MenuOption->Sequence == 2) {
646 PrintChar (RIGHT_NUMERIC_DELIMITER);
647 }
648 }
649
650 goto EnterCarriageReturn;
651 break;
652
653 case SCAN_UP:
654 case SCAN_DOWN:
655 goto EnterCarriageReturn;
656
657 case SCAN_ESC:
658 return EFI_DEVICE_ERROR;
659
660 default:
661 break;
662 }
663
664 break;
665
666 EnterCarriageReturn:
667
668 case CHAR_CARRIAGE_RETURN:
669 //
670 // Store Edit value back to Question
671 //
672 if (Question->Operand == EFI_IFR_DATE_OP) {
673 switch (MenuOption->Sequence) {
674 case 0:
675 QuestionValue->Value.date.Month = (UINT8) EditValue;
676 break;
677
678 case 1:
679 QuestionValue->Value.date.Day = (UINT8) EditValue;
680 break;
681
682 case 2:
683 QuestionValue->Value.date.Year = (UINT16) EditValue;
684 break;
685
686 default:
687 break;
688 }
689 } else if (Question->Operand == EFI_IFR_TIME_OP) {
690 switch (MenuOption->Sequence) {
691 case 0:
692 QuestionValue->Value.time.Hour = (UINT8) EditValue;
693 break;
694
695 case 1:
696 QuestionValue->Value.time.Minute = (UINT8) EditValue;
697 break;
698
699 case 2:
700 QuestionValue->Value.time.Second = (UINT8) EditValue;
701 break;
702
703 default:
704 break;
705 }
706 } else {
707 //
708 // Numeric
709 //
710 QuestionValue->Value.u64 = EditValue;
711 }
712
713 //
714 // Adjust the value to the correct one.
715 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
716 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
717 //
718 if (Question->Operand == EFI_IFR_DATE_OP &&
719 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
720 AdjustQuestionValue (Question, (UINT8)MenuOption->Sequence);
721 }
722
723 //
724 // Check to see if the Value is something reasonable against consistency limitations.
725 // If not, let's kick the error specified.
726 //
727 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
728 if (EFI_ERROR (Status)) {
729 //
730 // Input value is not valid, restore Question Value
731 //
732 GetQuestionValue (FormSet, Form, Question, TRUE);
733 } else {
734 SetQuestionValue (FormSet, Form, Question, TRUE);
735 if (!DateOrTime || (Question->Storage != NULL)) {
736 //
737 // NV flag is unnecessary for RTC type of Date/Time
738 //
739 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
740 }
741 }
742
743 return Status;
744 break;
745
746 case CHAR_BACKSPACE:
747 if (ManualInput) {
748 if (Count == 0) {
749 break;
750 }
751 //
752 // Remove a character
753 //
754 EditValue = PreviousNumber[Count - 1];
755 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
756 Count--;
757 Column--;
758 PrintAt (Column, Row, L" ");
759 }
760 break;
761
762 default:
763 if (ManualInput) {
764 if (HexInput) {
765 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
766 Digital = (UINT8) (Key.UnicodeChar - L'0');
767 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
768 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
769 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
770 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
771 } else {
772 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
773 break;
774 }
775 } else {
776 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
777 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
778 break;
779 }
780 }
781
782 //
783 // If Count exceed input width, there is no way more is valid
784 //
785 if (Count >= InputWidth) {
786 break;
787 }
788 //
789 // Someone typed something valid!
790 //
791 if (Count != 0) {
792 if (HexInput) {
793 EditValue = LShiftU64 (EditValue, 4) + Digital;
794 } else {
795 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
796 }
797 } else {
798 if (HexInput) {
799 EditValue = Digital;
800 } else {
801 EditValue = Key.UnicodeChar - L'0';
802 }
803 }
804
805 if (EditValue > Maximum) {
806 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, TRUE);
807 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
808 EditValue = PreviousNumber[Count];
809 break;
810 } else {
811 UpdateStatusBar (Selection, INPUT_ERROR, Question->QuestionFlags, FALSE);
812 }
813
814 Count++;
815 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
816 PreviousNumber[Count] = EditValue;
817
818 PrintCharAt (Column, Row, Key.UnicodeChar);
819 Column++;
820 }
821 break;
822 }
823 } while (TRUE);
824
825 }
826
827
828 /**
829 Get selection for OneOf and OrderedList (Left/Right will be ignored).
830
831 @param Selection Pointer to current selection.
832 @param MenuOption Pointer to the current input menu.
833
834 @retval EFI_SUCCESS If Option input is processed successfully
835 @retval EFI_DEVICE_ERROR If operation fails
836
837 **/
838 EFI_STATUS
839 GetSelectionInputPopUp (
840 IN UI_MENU_SELECTION *Selection,
841 IN UI_MENU_OPTION *MenuOption
842 )
843 {
844 EFI_STATUS Status;
845 EFI_INPUT_KEY Key;
846 UINTN Index;
847 CHAR16 *StringPtr;
848 CHAR16 *TempStringPtr;
849 UINTN Index2;
850 UINTN TopOptionIndex;
851 UINTN HighlightOptionIndex;
852 UINTN Start;
853 UINTN End;
854 UINTN Top;
855 UINTN Bottom;
856 UINTN PopUpMenuLines;
857 UINTN MenuLinesInView;
858 UINTN PopUpWidth;
859 CHAR16 Character;
860 INT32 SavedAttribute;
861 BOOLEAN ShowDownArrow;
862 BOOLEAN ShowUpArrow;
863 UINTN DimensionsWidth;
864 LIST_ENTRY *Link;
865 BOOLEAN OrderedList;
866 UINT8 *ValueArray;
867 UINT8 ValueType;
868 EFI_HII_VALUE HiiValue;
869 EFI_HII_VALUE *HiiValueArray;
870 UINTN OptionCount;
871 QUESTION_OPTION *OneOfOption;
872 QUESTION_OPTION *CurrentOption;
873 FORM_BROWSER_STATEMENT *Question;
874
875 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
876
877 ValueArray = NULL;
878 ValueType = 0;
879 CurrentOption = NULL;
880 ShowDownArrow = FALSE;
881 ShowUpArrow = FALSE;
882
883 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
884 ASSERT (StringPtr);
885
886 Question = MenuOption->ThisTag;
887 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {
888 ValueArray = Question->BufferValue;
889 ValueType = Question->ValueType;
890 OrderedList = TRUE;
891 } else {
892 OrderedList = FALSE;
893 }
894
895 //
896 // Calculate Option count
897 //
898 if (OrderedList) {
899 for (Index = 0; Index < Question->MaxContainers; Index++) {
900 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
901 break;
902 }
903 }
904
905 OptionCount = Index;
906 } else {
907 OptionCount = 0;
908 Link = GetFirstNode (&Question->OptionListHead);
909 while (!IsNull (&Question->OptionListHead, Link)) {
910 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
911
912 OptionCount++;
913
914 Link = GetNextNode (&Question->OptionListHead, Link);
915 }
916 }
917
918 //
919 // Prepare HiiValue array
920 //
921 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));
922 ASSERT (HiiValueArray != NULL);
923 Link = GetFirstNode (&Question->OptionListHead);
924 for (Index = 0; Index < OptionCount; Index++) {
925 if (OrderedList) {
926 HiiValueArray[Index].Type = ValueType;
927 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
928 } else {
929 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
930 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));
931 Link = GetNextNode (&Question->OptionListHead, Link);
932 }
933 }
934
935 //
936 // Move Suppressed Option to list tail
937 //
938 PopUpMenuLines = 0;
939 for (Index = 0; Index < OptionCount; Index++) {
940 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);
941 if (OneOfOption == NULL) {
942 return EFI_NOT_FOUND;
943 }
944
945 RemoveEntryList (&OneOfOption->Link);
946
947 if ((OneOfOption->SuppressExpression != NULL) &&
948 (OneOfOption->SuppressExpression->Result.Value.b)) {
949 //
950 // This option is suppressed, insert to tail
951 //
952 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
953 } else {
954 //
955 // Insert to head
956 //
957 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
958
959 PopUpMenuLines++;
960 }
961 }
962
963 //
964 // Get the number of one of options present and its size
965 //
966 PopUpWidth = 0;
967 HighlightOptionIndex = 0;
968 Link = GetFirstNode (&Question->OptionListHead);
969 for (Index = 0; Index < PopUpMenuLines; Index++) {
970 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
971
972 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
973 if (StrLen (StringPtr) > PopUpWidth) {
974 PopUpWidth = StrLen (StringPtr);
975 }
976 FreePool (StringPtr);
977
978 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {
979 //
980 // Find current selected Option for OneOf
981 //
982 HighlightOptionIndex = Index;
983 }
984
985 Link = GetNextNode (&Question->OptionListHead, Link);
986 }
987
988 //
989 // Perform popup menu initialization.
990 //
991 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
992
993 SavedAttribute = gST->ConOut->Mode->Attribute;
994 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
995
996 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
997 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
998 }
999
1000 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;
1001 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1002 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;
1003 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;
1004
1005 MenuLinesInView = Bottom - Top - 1;
1006 if (MenuLinesInView >= PopUpMenuLines) {
1007 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1008 Bottom = Top + PopUpMenuLines + 1;
1009 } else {
1010 ShowDownArrow = TRUE;
1011 }
1012
1013 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1014 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1015 } else {
1016 TopOptionIndex = 0;
1017 }
1018
1019 do {
1020 //
1021 // Clear that portion of the screen
1022 //
1023 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);
1024
1025 //
1026 // Draw "One of" pop-up menu
1027 //
1028 Character = BOXDRAW_DOWN_RIGHT;
1029 PrintCharAt (Start, Top, Character);
1030 for (Index = Start; Index + 2 < End; Index++) {
1031 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1032 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1033 } else {
1034 Character = BOXDRAW_HORIZONTAL;
1035 }
1036
1037 PrintChar (Character);
1038 }
1039
1040 Character = BOXDRAW_DOWN_LEFT;
1041 PrintChar (Character);
1042 Character = BOXDRAW_VERTICAL;
1043 for (Index = Top + 1; Index < Bottom; Index++) {
1044 PrintCharAt (Start, Index, Character);
1045 PrintCharAt (End - 1, Index, Character);
1046 }
1047
1048 //
1049 // Move to top Option
1050 //
1051 Link = GetFirstNode (&Question->OptionListHead);
1052 for (Index = 0; Index < TopOptionIndex; Index++) {
1053 Link = GetNextNode (&Question->OptionListHead, Link);
1054 }
1055
1056 //
1057 // Display the One of options
1058 //
1059 Index2 = Top + 1;
1060 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1061 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1062 Link = GetNextNode (&Question->OptionListHead, Link);
1063
1064 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);
1065 //
1066 // If the string occupies multiple lines, truncate it to fit in one line,
1067 // and append a "..." for indication.
1068 //
1069 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1070 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1071 ASSERT ( TempStringPtr != NULL );
1072 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1073 FreePool (StringPtr);
1074 StringPtr = TempStringPtr;
1075 StrCat (StringPtr, L"...");
1076 }
1077
1078 if (Index == HighlightOptionIndex) {
1079 //
1080 // Highlight the selected one
1081 //
1082 CurrentOption = OneOfOption;
1083
1084 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);
1085 PrintStringAt (Start + 2, Index2, StringPtr);
1086 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1087 } else {
1088 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1089 PrintStringAt (Start + 2, Index2, StringPtr);
1090 }
1091
1092 Index2++;
1093 FreePool (StringPtr);
1094 }
1095
1096 Character = BOXDRAW_UP_RIGHT;
1097 PrintCharAt (Start, Bottom, Character);
1098 for (Index = Start; Index + 2 < End; Index++) {
1099 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1100 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1101 } else {
1102 Character = BOXDRAW_HORIZONTAL;
1103 }
1104
1105 PrintChar (Character);
1106 }
1107
1108 Character = BOXDRAW_UP_LEFT;
1109 PrintChar (Character);
1110
1111 //
1112 // Get User selection
1113 //
1114 Key.UnicodeChar = CHAR_NULL;
1115 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1116 Key.ScanCode = gDirection;
1117 gDirection = 0;
1118 goto TheKey;
1119 }
1120
1121 Status = WaitForKeyStroke (&Key);
1122
1123 TheKey:
1124 switch (Key.UnicodeChar) {
1125 case '+':
1126 if (OrderedList) {
1127 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1128 //
1129 // Highlight reaches the top of the popup window, scroll one menu item.
1130 //
1131 TopOptionIndex--;
1132 ShowDownArrow = TRUE;
1133 }
1134
1135 if (TopOptionIndex == 0) {
1136 ShowUpArrow = FALSE;
1137 }
1138
1139 if (HighlightOptionIndex > 0) {
1140 HighlightOptionIndex--;
1141
1142 ASSERT (CurrentOption != NULL);
1143 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1144 }
1145 }
1146 break;
1147
1148 case '-':
1149 //
1150 // If an ordered list op-code, we will allow for a popup of +/- keys
1151 // to create an ordered list of items
1152 //
1153 if (OrderedList) {
1154 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1155 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1156 //
1157 // Highlight reaches the bottom of the popup window, scroll one menu item.
1158 //
1159 TopOptionIndex++;
1160 ShowUpArrow = TRUE;
1161 }
1162
1163 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1164 ShowDownArrow = FALSE;
1165 }
1166
1167 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1168 HighlightOptionIndex++;
1169
1170 ASSERT (CurrentOption != NULL);
1171 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1172 }
1173 }
1174 break;
1175
1176 case CHAR_NULL:
1177 switch (Key.ScanCode) {
1178 case SCAN_UP:
1179 case SCAN_DOWN:
1180 if (Key.ScanCode == SCAN_UP) {
1181 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1182 //
1183 // Highlight reaches the top of the popup window, scroll one menu item.
1184 //
1185 TopOptionIndex--;
1186 ShowDownArrow = TRUE;
1187 }
1188
1189 if (TopOptionIndex == 0) {
1190 ShowUpArrow = FALSE;
1191 }
1192
1193 if (HighlightOptionIndex > 0) {
1194 HighlightOptionIndex--;
1195 }
1196 } else {
1197 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1198 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1199 //
1200 // Highlight reaches the bottom of the popup window, scroll one menu item.
1201 //
1202 TopOptionIndex++;
1203 ShowUpArrow = TRUE;
1204 }
1205
1206 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1207 ShowDownArrow = FALSE;
1208 }
1209
1210 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1211 HighlightOptionIndex++;
1212 }
1213 }
1214 break;
1215
1216 case SCAN_ESC:
1217 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1218
1219 //
1220 // Restore link list order for orderedlist
1221 //
1222 if (OrderedList) {
1223 HiiValue.Type = ValueType;
1224 HiiValue.Value.u64 = 0;
1225 for (Index = 0; Index < Question->MaxContainers; Index++) {
1226 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1227 if (HiiValue.Value.u64 == 0) {
1228 break;
1229 }
1230
1231 OneOfOption = ValueToOption (Question, &HiiValue);
1232 if (OneOfOption == NULL) {
1233 return EFI_NOT_FOUND;
1234 }
1235
1236 RemoveEntryList (&OneOfOption->Link);
1237 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1238 }
1239 }
1240
1241 FreePool (HiiValueArray);
1242 return EFI_DEVICE_ERROR;
1243
1244 default:
1245 break;
1246 }
1247
1248 break;
1249
1250 case CHAR_CARRIAGE_RETURN:
1251 //
1252 // return the current selection
1253 //
1254 if (OrderedList) {
1255 Index = 0;
1256 Link = GetFirstNode (&Question->OptionListHead);
1257 while (!IsNull (&Question->OptionListHead, Link)) {
1258 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);
1259
1260 SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);
1261
1262 Index++;
1263 if (Index > Question->MaxContainers) {
1264 break;
1265 }
1266
1267 Link = GetNextNode (&Question->OptionListHead, Link);
1268 }
1269 } else {
1270 ASSERT (CurrentOption != NULL);
1271 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));
1272 }
1273
1274 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1275 FreePool (HiiValueArray);
1276
1277 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);
1278 if (EFI_ERROR (Status)) {
1279 //
1280 // Input value is not valid, restore Question Value
1281 //
1282 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1283 } else {
1284 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);
1285 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);
1286 }
1287
1288 return Status;
1289
1290 default:
1291 break;
1292 }
1293 } while (TRUE);
1294
1295 }
1296
1297 /**
1298 Wait for a key to be pressed by user.
1299
1300 @param Key The key which is pressed by user.
1301
1302 @retval EFI_SUCCESS The function always completed successfully.
1303
1304 **/
1305 EFI_STATUS
1306 WaitForKeyStroke (
1307 OUT EFI_INPUT_KEY *Key
1308 )
1309 {
1310 EFI_STATUS Status;
1311
1312 do {
1313 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);
1314 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);
1315 } while (EFI_ERROR(Status));
1316
1317 return Status;
1318 }