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