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