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