]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
Fixed user input arrow down/ page down caused form display highlight menu error.
[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 UINTN Column;
443 UINTN Row;
444 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
445 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
446 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
447 UINTN Count;
448 UINTN Loop;
449 BOOLEAN ManualInput;
450 BOOLEAN HexInput;
451 BOOLEAN DateOrTime;
452 UINTN InputWidth;
453 UINT64 EditValue;
454 UINT64 Step;
455 UINT64 Minimum;
456 UINT64 Maximum;
457 UINTN EraseLen;
458 UINT8 Digital;
459 EFI_INPUT_KEY Key;
460 EFI_HII_VALUE *QuestionValue;
461 FORM_DISPLAY_ENGINE_STATEMENT *Question;
462 EFI_IFR_NUMERIC *NumericOp;
463 UINT16 StorageWidth;
464
465 Column = MenuOption->OptCol;
466 Row = MenuOption->Row;
467 PreviousNumber[0] = 0;
468 Count = 0;
469 InputWidth = 0;
470 Digital = 0;
471 StorageWidth = 0;
472 Minimum = 0;
473 Maximum = 0;
474 NumericOp = NULL;
475
476 Question = MenuOption->ThisTag;
477 QuestionValue = &Question->CurrentValue;
478
479 //
480 // Only two case, user can enter to this function: Enter and +/- case.
481 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
482 //
483 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
484
485 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
486 DateOrTime = TRUE;
487 } else {
488 DateOrTime = FALSE;
489 }
490
491 //
492 // Prepare Value to be edit
493 //
494 EraseLen = 0;
495 EditValue = 0;
496 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
497 Step = 1;
498 Minimum = 1;
499
500 switch (MenuOption->Sequence) {
501 case 0:
502 Maximum = 12;
503 EraseLen = 4;
504 EditValue = QuestionValue->Value.date.Month;
505 break;
506
507 case 1:
508 switch (QuestionValue->Value.date.Month) {
509 case 2:
510 if ((QuestionValue->Value.date.Year % 4) == 0 &&
511 ((QuestionValue->Value.date.Year % 100) != 0 ||
512 (QuestionValue->Value.date.Year % 400) == 0)) {
513 Maximum = 29;
514 } else {
515 Maximum = 28;
516 }
517 break;
518 case 4:
519 case 6:
520 case 9:
521 case 11:
522 Maximum = 30;
523 break;
524 default:
525 Maximum = 31;
526 break;
527 }
528
529 EraseLen = 3;
530 EditValue = QuestionValue->Value.date.Day;
531 break;
532
533 case 2:
534 Maximum = 0xffff;
535 EraseLen = 5;
536 EditValue = QuestionValue->Value.date.Year;
537 break;
538
539 default:
540 break;
541 }
542 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
543 Step = 1;
544 Minimum = 0;
545
546 switch (MenuOption->Sequence) {
547 case 0:
548 Maximum = 23;
549 EraseLen = 4;
550 EditValue = QuestionValue->Value.time.Hour;
551 break;
552
553 case 1:
554 Maximum = 59;
555 EraseLen = 3;
556 EditValue = QuestionValue->Value.time.Minute;
557 break;
558
559 case 2:
560 Maximum = 59;
561 EraseLen = 3;
562 EditValue = QuestionValue->Value.time.Second;
563 break;
564
565 default:
566 break;
567 }
568 } else {
569 ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
570 NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
571 GetValueFromNum(Question->OpCode, &Minimum, &Maximum, &Step, &StorageWidth);
572 EditValue = QuestionValue->Value.u64;
573 EraseLen = gOptionBlockWidth;
574 }
575
576 if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL) &&
577 ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {
578 HexInput = TRUE;
579 } else {
580 HexInput = FALSE;
581 }
582
583 //
584 // Enter from "Enter" input, clear the old word showing.
585 //
586 if (ManualInput) {
587 if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
588 if (HexInput) {
589 InputWidth = StorageWidth * 2;
590 } else {
591 switch (StorageWidth) {
592 case 1:
593 InputWidth = 3;
594 break;
595
596 case 2:
597 InputWidth = 5;
598 break;
599
600 case 4:
601 InputWidth = 10;
602 break;
603
604 case 8:
605 InputWidth = 20;
606 break;
607
608 default:
609 InputWidth = 0;
610 break;
611 }
612 }
613
614 InputText[0] = LEFT_NUMERIC_DELIMITER;
615 SetUnicodeMem (InputText + 1, InputWidth, L' ');
616 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
617 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
618 InputText[InputWidth + 2] = L'\0';
619
620 PrintStringAt (Column, Row, InputText);
621 Column++;
622 }
623
624 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
625 if (MenuOption->Sequence == 2) {
626 InputWidth = 4;
627 } else {
628 InputWidth = 2;
629 }
630
631 if (MenuOption->Sequence == 0) {
632 InputText[0] = LEFT_NUMERIC_DELIMITER;
633 SetUnicodeMem (InputText + 1, InputWidth, L' ');
634 } else {
635 SetUnicodeMem (InputText, InputWidth, L' ');
636 }
637
638 if (MenuOption->Sequence == 2) {
639 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
640 } else {
641 InputText[InputWidth + 1] = DATE_SEPARATOR;
642 }
643 InputText[InputWidth + 2] = L'\0';
644
645 PrintStringAt (Column, Row, InputText);
646 if (MenuOption->Sequence == 0) {
647 Column++;
648 }
649 }
650
651 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
652 InputWidth = 2;
653
654 if (MenuOption->Sequence == 0) {
655 InputText[0] = LEFT_NUMERIC_DELIMITER;
656 SetUnicodeMem (InputText + 1, InputWidth, L' ');
657 } else {
658 SetUnicodeMem (InputText, InputWidth, L' ');
659 }
660
661 if (MenuOption->Sequence == 2) {
662 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
663 } else {
664 InputText[InputWidth + 1] = TIME_SEPARATOR;
665 }
666 InputText[InputWidth + 2] = L'\0';
667
668 PrintStringAt (Column, Row, InputText);
669 if (MenuOption->Sequence == 0) {
670 Column++;
671 }
672 }
673 }
674
675 //
676 // First time we enter this handler, we need to check to see if
677 // we were passed an increment or decrement directive
678 //
679 do {
680 Key.UnicodeChar = CHAR_NULL;
681 if (gDirection != 0) {
682 Key.ScanCode = gDirection;
683 gDirection = 0;
684 goto TheKey2;
685 }
686
687 WaitForKeyStroke (&Key);
688
689 TheKey2:
690 switch (Key.UnicodeChar) {
691
692 case '+':
693 case '-':
694 if (Key.UnicodeChar == '+') {
695 Key.ScanCode = SCAN_RIGHT;
696 } else {
697 Key.ScanCode = SCAN_LEFT;
698 }
699 Key.UnicodeChar = CHAR_NULL;
700 goto TheKey2;
701
702 case CHAR_NULL:
703 switch (Key.ScanCode) {
704 case SCAN_LEFT:
705 case SCAN_RIGHT:
706 if (DateOrTime && !ManualInput) {
707 //
708 // By setting this value, we will return back to the caller.
709 // We need to do this since an auto-refresh will destroy the adjustment
710 // based on what the real-time-clock is showing. So we always commit
711 // upon changing the value.
712 //
713 gDirection = SCAN_DOWN;
714 }
715
716 if ((Step != 0) && !ManualInput) {
717 if (Key.ScanCode == SCAN_LEFT) {
718 if (EditValue >= Minimum + Step) {
719 EditValue = EditValue - Step;
720 } else if (EditValue > Minimum){
721 EditValue = Minimum;
722 } else {
723 EditValue = Maximum;
724 }
725 } else if (Key.ScanCode == SCAN_RIGHT) {
726 if (EditValue + Step <= Maximum) {
727 EditValue = EditValue + Step;
728 } else if (EditValue < Maximum) {
729 EditValue = Maximum;
730 } else {
731 EditValue = Minimum;
732 }
733 }
734
735 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
736 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
737 if (MenuOption->Sequence == 2) {
738 //
739 // Year
740 //
741 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
742 } else {
743 //
744 // Month/Day
745 //
746 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
747 }
748
749 if (MenuOption->Sequence == 0) {
750 ASSERT (EraseLen >= 2);
751 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
752 } else if (MenuOption->Sequence == 1) {
753 ASSERT (EraseLen >= 1);
754 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
755 }
756 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
757 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
758
759 if (MenuOption->Sequence == 0) {
760 ASSERT (EraseLen >= 2);
761 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
762 } else if (MenuOption->Sequence == 1) {
763 ASSERT (EraseLen >= 1);
764 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
765 }
766 } else {
767 QuestionValue->Value.u64 = EditValue;
768 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
769 }
770
771 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
772 for (Loop = 0; Loop < EraseLen; Loop++) {
773 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
774 }
775 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
776
777 if (MenuOption->Sequence == 0) {
778 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
779 Column = MenuOption->OptCol + 1;
780 }
781
782 PrintStringAt (Column, Row, FormattedNumber);
783
784 if (!DateOrTime || MenuOption->Sequence == 2) {
785 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
786 }
787 }
788
789 goto EnterCarriageReturn;
790 break;
791
792 case SCAN_UP:
793 case SCAN_DOWN:
794 goto EnterCarriageReturn;
795
796 case SCAN_ESC:
797 return EFI_DEVICE_ERROR;
798
799 default:
800 break;
801 }
802
803 break;
804
805 EnterCarriageReturn:
806
807 case CHAR_CARRIAGE_RETURN:
808 //
809 // Validate input value with Minimum value.
810 //
811 if (EditValue < Minimum) {
812 UpdateStatusBar (INPUT_ERROR, TRUE);
813 break;
814 } else {
815 UpdateStatusBar (INPUT_ERROR, FALSE);
816 }
817
818 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
819 QuestionValue = &gUserInput->InputValue;
820 //
821 // Store Edit value back to Question
822 //
823 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
824 switch (MenuOption->Sequence) {
825 case 0:
826 QuestionValue->Value.date.Month = (UINT8) EditValue;
827 break;
828
829 case 1:
830 QuestionValue->Value.date.Day = (UINT8) EditValue;
831 break;
832
833 case 2:
834 QuestionValue->Value.date.Year = (UINT16) EditValue;
835 break;
836
837 default:
838 break;
839 }
840 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
841 switch (MenuOption->Sequence) {
842 case 0:
843 QuestionValue->Value.time.Hour = (UINT8) EditValue;
844 break;
845
846 case 1:
847 QuestionValue->Value.time.Minute = (UINT8) EditValue;
848 break;
849
850 case 2:
851 QuestionValue->Value.time.Second = (UINT8) EditValue;
852 break;
853
854 default:
855 break;
856 }
857 } else {
858 //
859 // Numeric
860 //
861 QuestionValue->Value.u64 = EditValue;
862 }
863
864 //
865 // Adjust the value to the correct one.
866 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
867 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
868 //
869 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP &&
870 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
871 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
872 }
873
874 return EFI_SUCCESS;
875 break;
876
877 case CHAR_BACKSPACE:
878 if (ManualInput) {
879 if (Count == 0) {
880 break;
881 }
882 //
883 // Remove a character
884 //
885 EditValue = PreviousNumber[Count - 1];
886 UpdateStatusBar (INPUT_ERROR, FALSE);
887 Count--;
888 Column--;
889 PrintStringAt (Column, Row, L" ");
890 }
891 break;
892
893 default:
894 if (ManualInput) {
895 if (HexInput) {
896 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
897 Digital = (UINT8) (Key.UnicodeChar - L'0');
898 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
899 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
900 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
901 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
902 } else {
903 UpdateStatusBar (INPUT_ERROR, TRUE);
904 break;
905 }
906 } else {
907 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
908 UpdateStatusBar (INPUT_ERROR, TRUE);
909 break;
910 }
911 }
912
913 //
914 // If Count exceed input width, there is no way more is valid
915 //
916 if (Count >= InputWidth) {
917 break;
918 }
919 //
920 // Someone typed something valid!
921 //
922 if (Count != 0) {
923 if (HexInput) {
924 EditValue = LShiftU64 (EditValue, 4) + Digital;
925 } else {
926 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
927 }
928 } else {
929 if (HexInput) {
930 EditValue = Digital;
931 } else {
932 EditValue = Key.UnicodeChar - L'0';
933 }
934 }
935
936 if (EditValue > Maximum) {
937 UpdateStatusBar (INPUT_ERROR, TRUE);
938 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
939 EditValue = PreviousNumber[Count];
940 break;
941 } else {
942 UpdateStatusBar (INPUT_ERROR, FALSE);
943 }
944
945 Count++;
946 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
947 PreviousNumber[Count] = EditValue;
948
949 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
950 PrintCharAt (Column, Row, Key.UnicodeChar);
951 Column++;
952 }
953 break;
954 }
955 } while (TRUE);
956 }
957
958 /**
959 Adjust option order base on the question value.
960
961 @param Question Pointer to current question.
962 @param PopUpMenuLines The line number of the pop up menu.
963
964 @retval EFI_SUCCESS If Option input is processed successfully
965 @retval EFI_DEVICE_ERROR If operation fails
966
967 **/
968 EFI_STATUS
969 AdjustOptionOrder (
970 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
971 OUT UINTN *PopUpMenuLines
972 )
973 {
974 UINTN Index;
975 EFI_IFR_ORDERED_LIST *OrderList;
976 UINT8 *ValueArray;
977 UINT8 ValueType;
978 LIST_ENTRY *Link;
979 DISPLAY_QUESTION_OPTION *OneOfOption;
980 EFI_HII_VALUE *HiiValueArray;
981
982 Link = GetFirstNode (&Question->OptionListHead);
983 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
984 ValueArray = Question->CurrentValue.Buffer;
985 ValueType = OneOfOption->OptionOpCode->Type;
986 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
987
988 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
989 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
990 break;
991 }
992 }
993
994 *PopUpMenuLines = Index;
995
996 //
997 // Prepare HiiValue array
998 //
999 HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
1000 ASSERT (HiiValueArray != NULL);
1001
1002 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1003 HiiValueArray[Index].Type = ValueType;
1004 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1005 }
1006
1007 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1008 OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
1009 if (OneOfOption == NULL) {
1010 return EFI_NOT_FOUND;
1011 }
1012
1013 RemoveEntryList (&OneOfOption->Link);
1014
1015 //
1016 // Insert to head.
1017 //
1018 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
1019 }
1020
1021 FreePool (HiiValueArray);
1022
1023 return EFI_SUCCESS;
1024 }
1025
1026 /**
1027 Base on the type to compare the value.
1028
1029 @param Value1 The first value need to compare.
1030 @param Value2 The second value need to compare.
1031 @param Type The value type for above two values.
1032
1033 @retval TRUE The two value are same.
1034 @retval FALSE The two value are different.
1035
1036 **/
1037 BOOLEAN
1038 IsValuesEqual (
1039 IN EFI_IFR_TYPE_VALUE *Value1,
1040 IN EFI_IFR_TYPE_VALUE *Value2,
1041 IN UINT8 Type
1042 )
1043 {
1044 switch (Type) {
1045 case EFI_IFR_TYPE_BOOLEAN:
1046 case EFI_IFR_TYPE_NUM_SIZE_8:
1047 return (BOOLEAN) (Value1->u8 == Value2->u8);
1048
1049 case EFI_IFR_TYPE_NUM_SIZE_16:
1050 return (BOOLEAN) (Value1->u16 == Value2->u16);
1051
1052 case EFI_IFR_TYPE_NUM_SIZE_32:
1053 return (BOOLEAN) (Value1->u32 == Value2->u32);
1054
1055 case EFI_IFR_TYPE_NUM_SIZE_64:
1056 return (BOOLEAN) (Value1->u64 == Value2->u64);
1057
1058 default:
1059 ASSERT (FALSE);
1060 return FALSE;
1061 }
1062 }
1063
1064 /**
1065 Base on the type to set the value.
1066
1067 @param Dest The dest value.
1068 @param Source The source value.
1069 @param Type The value type for above two values.
1070
1071 **/
1072 VOID
1073 SetValuesByType (
1074 OUT EFI_IFR_TYPE_VALUE *Dest,
1075 IN EFI_IFR_TYPE_VALUE *Source,
1076 IN UINT8 Type
1077 )
1078 {
1079 switch (Type) {
1080 case EFI_IFR_TYPE_BOOLEAN:
1081 Dest->b = Source->b;
1082 break;
1083
1084 case EFI_IFR_TYPE_NUM_SIZE_8:
1085 Dest->u8 = Source->u8;
1086 break;
1087
1088 case EFI_IFR_TYPE_NUM_SIZE_16:
1089 Dest->u16 = Source->u16;
1090 break;
1091
1092 case EFI_IFR_TYPE_NUM_SIZE_32:
1093 Dest->u32 = Source->u32;
1094 break;
1095
1096 case EFI_IFR_TYPE_NUM_SIZE_64:
1097 Dest->u64 = Source->u64;
1098 break;
1099
1100 default:
1101 ASSERT (FALSE);
1102 break;
1103 }
1104 }
1105
1106 /**
1107 Get selection for OneOf and OrderedList (Left/Right will be ignored).
1108
1109 @param MenuOption Pointer to the current input menu.
1110
1111 @retval EFI_SUCCESS If Option input is processed successfully
1112 @retval EFI_DEVICE_ERROR If operation fails
1113
1114 **/
1115 EFI_STATUS
1116 GetSelectionInputPopUp (
1117 IN UI_MENU_OPTION *MenuOption
1118 )
1119 {
1120 EFI_INPUT_KEY Key;
1121 UINTN Index;
1122 CHAR16 *StringPtr;
1123 CHAR16 *TempStringPtr;
1124 UINTN Index2;
1125 UINTN TopOptionIndex;
1126 UINTN HighlightOptionIndex;
1127 UINTN Start;
1128 UINTN End;
1129 UINTN Top;
1130 UINTN Bottom;
1131 UINTN PopUpMenuLines;
1132 UINTN MenuLinesInView;
1133 UINTN PopUpWidth;
1134 CHAR16 Character;
1135 INT32 SavedAttribute;
1136 BOOLEAN ShowDownArrow;
1137 BOOLEAN ShowUpArrow;
1138 UINTN DimensionsWidth;
1139 LIST_ENTRY *Link;
1140 BOOLEAN OrderedList;
1141 UINT8 *ValueArray;
1142 UINT8 *ReturnValue;
1143 UINT8 ValueType;
1144 EFI_HII_VALUE HiiValue;
1145 DISPLAY_QUESTION_OPTION *OneOfOption;
1146 DISPLAY_QUESTION_OPTION *CurrentOption;
1147 FORM_DISPLAY_ENGINE_STATEMENT *Question;
1148 INTN Result;
1149 EFI_IFR_ORDERED_LIST *OrderList;
1150
1151 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
1152
1153 ValueArray = NULL;
1154 ValueType = 0;
1155 CurrentOption = NULL;
1156 ShowDownArrow = FALSE;
1157 ShowUpArrow = FALSE;
1158
1159 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
1160 ASSERT (StringPtr);
1161
1162 ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
1163
1164 Question = MenuOption->ThisTag;
1165 if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
1166 Link = GetFirstNode (&Question->OptionListHead);
1167 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1168 ValueArray = Question->CurrentValue.Buffer;
1169 ValueType = OneOfOption->OptionOpCode->Type;
1170 OrderedList = TRUE;
1171 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
1172 } else {
1173 OrderedList = FALSE;
1174 OrderList = NULL;
1175 }
1176
1177 //
1178 // Calculate Option count
1179 //
1180 PopUpMenuLines = 0;
1181 if (OrderedList) {
1182 AdjustOptionOrder(Question, &PopUpMenuLines);
1183 } else {
1184 Link = GetFirstNode (&Question->OptionListHead);
1185 while (!IsNull (&Question->OptionListHead, Link)) {
1186 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1187 PopUpMenuLines++;
1188 Link = GetNextNode (&Question->OptionListHead, Link);
1189 }
1190 }
1191
1192 //
1193 // Get the number of one of options present and its size
1194 //
1195 PopUpWidth = 0;
1196 HighlightOptionIndex = 0;
1197 Link = GetFirstNode (&Question->OptionListHead);
1198 for (Index = 0; Index < PopUpMenuLines; Index++) {
1199 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1200
1201 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1202 if (StrLen (StringPtr) > PopUpWidth) {
1203 PopUpWidth = StrLen (StringPtr);
1204 }
1205 FreePool (StringPtr);
1206 HiiValue.Type = OneOfOption->OptionOpCode->Type;
1207 SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
1208 if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
1209 //
1210 // Find current selected Option for OneOf
1211 //
1212 HighlightOptionIndex = Index;
1213 }
1214
1215 Link = GetNextNode (&Question->OptionListHead, Link);
1216 }
1217
1218 //
1219 // Perform popup menu initialization.
1220 //
1221 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1222
1223 SavedAttribute = gST->ConOut->Mode->Attribute;
1224 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1225
1226 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1227 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1228 }
1229
1230 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
1231 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1232 Top = gStatementDimensions.TopRow;
1233 Bottom = gStatementDimensions.BottomRow - 1;
1234
1235 MenuLinesInView = Bottom - Top - 1;
1236 if (MenuLinesInView >= PopUpMenuLines) {
1237 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1238 Bottom = Top + PopUpMenuLines + 1;
1239 } else {
1240 ShowDownArrow = TRUE;
1241 }
1242
1243 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1244 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1245 } else {
1246 TopOptionIndex = 0;
1247 }
1248
1249 do {
1250 //
1251 // Clear that portion of the screen
1252 //
1253 ClearLines (Start, End, Top, Bottom, GetPopupColor ());
1254
1255 //
1256 // Draw "One of" pop-up menu
1257 //
1258 Character = BOXDRAW_DOWN_RIGHT;
1259 PrintCharAt (Start, Top, Character);
1260 for (Index = Start; Index + 2 < End; Index++) {
1261 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1262 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1263 } else {
1264 Character = BOXDRAW_HORIZONTAL;
1265 }
1266
1267 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1268 }
1269
1270 Character = BOXDRAW_DOWN_LEFT;
1271 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1272 Character = BOXDRAW_VERTICAL;
1273 for (Index = Top + 1; Index < Bottom; Index++) {
1274 PrintCharAt (Start, Index, Character);
1275 PrintCharAt (End - 1, Index, Character);
1276 }
1277
1278 //
1279 // Move to top Option
1280 //
1281 Link = GetFirstNode (&Question->OptionListHead);
1282 for (Index = 0; Index < TopOptionIndex; Index++) {
1283 Link = GetNextNode (&Question->OptionListHead, Link);
1284 }
1285
1286 //
1287 // Display the One of options
1288 //
1289 Index2 = Top + 1;
1290 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1291 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1292 Link = GetNextNode (&Question->OptionListHead, Link);
1293
1294 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1295 ASSERT (StringPtr != NULL);
1296 //
1297 // If the string occupies multiple lines, truncate it to fit in one line,
1298 // and append a "..." for indication.
1299 //
1300 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1301 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1302 ASSERT ( TempStringPtr != NULL );
1303 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1304 FreePool (StringPtr);
1305 StringPtr = TempStringPtr;
1306 StrCat (StringPtr, L"...");
1307 }
1308
1309 if (Index == HighlightOptionIndex) {
1310 //
1311 // Highlight the selected one
1312 //
1313 CurrentOption = OneOfOption;
1314
1315 gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
1316 PrintStringAt (Start + 2, Index2, StringPtr);
1317 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1318 } else {
1319 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1320 PrintStringAt (Start + 2, Index2, StringPtr);
1321 }
1322
1323 Index2++;
1324 FreePool (StringPtr);
1325 }
1326
1327 Character = BOXDRAW_UP_RIGHT;
1328 PrintCharAt (Start, Bottom, Character);
1329 for (Index = Start; Index + 2 < End; Index++) {
1330 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1331 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1332 } else {
1333 Character = BOXDRAW_HORIZONTAL;
1334 }
1335
1336 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1337 }
1338
1339 Character = BOXDRAW_UP_LEFT;
1340 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1341
1342 //
1343 // Get User selection
1344 //
1345 Key.UnicodeChar = CHAR_NULL;
1346 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1347 Key.ScanCode = gDirection;
1348 gDirection = 0;
1349 goto TheKey;
1350 }
1351
1352 WaitForKeyStroke (&Key);
1353
1354 TheKey:
1355 switch (Key.UnicodeChar) {
1356 case '+':
1357 if (OrderedList) {
1358 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1359 //
1360 // Highlight reaches the top of the popup window, scroll one menu item.
1361 //
1362 TopOptionIndex--;
1363 ShowDownArrow = TRUE;
1364 }
1365
1366 if (TopOptionIndex == 0) {
1367 ShowUpArrow = FALSE;
1368 }
1369
1370 if (HighlightOptionIndex > 0) {
1371 HighlightOptionIndex--;
1372
1373 ASSERT (CurrentOption != NULL);
1374 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1375 }
1376 }
1377 break;
1378
1379 case '-':
1380 //
1381 // If an ordered list op-code, we will allow for a popup of +/- keys
1382 // to create an ordered list of items
1383 //
1384 if (OrderedList) {
1385 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1386 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1387 //
1388 // Highlight reaches the bottom of the popup window, scroll one menu item.
1389 //
1390 TopOptionIndex++;
1391 ShowUpArrow = TRUE;
1392 }
1393
1394 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1395 ShowDownArrow = FALSE;
1396 }
1397
1398 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1399 HighlightOptionIndex++;
1400
1401 ASSERT (CurrentOption != NULL);
1402 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1403 }
1404 }
1405 break;
1406
1407 case CHAR_NULL:
1408 switch (Key.ScanCode) {
1409 case SCAN_UP:
1410 case SCAN_DOWN:
1411 if (Key.ScanCode == SCAN_UP) {
1412 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1413 //
1414 // Highlight reaches the top of the popup window, scroll one menu item.
1415 //
1416 TopOptionIndex--;
1417 ShowDownArrow = TRUE;
1418 }
1419
1420 if (TopOptionIndex == 0) {
1421 ShowUpArrow = FALSE;
1422 }
1423
1424 if (HighlightOptionIndex > 0) {
1425 HighlightOptionIndex--;
1426 }
1427 } else {
1428 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1429 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1430 //
1431 // Highlight reaches the bottom of the popup window, scroll one menu item.
1432 //
1433 TopOptionIndex++;
1434 ShowUpArrow = TRUE;
1435 }
1436
1437 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1438 ShowDownArrow = FALSE;
1439 }
1440
1441 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1442 HighlightOptionIndex++;
1443 }
1444 }
1445 break;
1446
1447 case SCAN_ESC:
1448 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1449
1450 //
1451 // Restore link list order for orderedlist
1452 //
1453 if (OrderedList) {
1454 HiiValue.Type = ValueType;
1455 HiiValue.Value.u64 = 0;
1456 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1457 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1458 if (HiiValue.Value.u64 == 0) {
1459 break;
1460 }
1461
1462 OneOfOption = ValueToOption (Question, &HiiValue);
1463 if (OneOfOption == NULL) {
1464 return EFI_NOT_FOUND;
1465 }
1466
1467 RemoveEntryList (&OneOfOption->Link);
1468 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1469 }
1470 }
1471
1472 return EFI_DEVICE_ERROR;
1473
1474 default:
1475 break;
1476 }
1477
1478 break;
1479
1480 case CHAR_CARRIAGE_RETURN:
1481 //
1482 // return the current selection
1483 //
1484 if (OrderedList) {
1485 ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
1486 ASSERT (ReturnValue != NULL);
1487 Index = 0;
1488 Link = GetFirstNode (&Question->OptionListHead);
1489 while (!IsNull (&Question->OptionListHead, Link)) {
1490 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1491 Link = GetNextNode (&Question->OptionListHead, Link);
1492
1493 SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
1494
1495 Index++;
1496 if (Index > OrderList->MaxContainers) {
1497 break;
1498 }
1499 }
1500 if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
1501 FreePool (ReturnValue);
1502 return EFI_DEVICE_ERROR;
1503 } else {
1504 gUserInput->InputValue.Buffer = ReturnValue;
1505 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1506 }
1507 } else {
1508 ASSERT (CurrentOption != NULL);
1509 gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
1510 if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
1511 return EFI_DEVICE_ERROR;
1512 } else {
1513 SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
1514 }
1515 }
1516
1517 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1518
1519 return EFI_SUCCESS;
1520
1521 default:
1522 break;
1523 }
1524 } while (TRUE);
1525
1526 }
1527