blob: f76937a442221295187e7c7ade31933a88d091e0 [file] [log] [blame]
Vishal Bhoj82c80712015-12-15 21:13:33 +05301/** @file
2Implementation for handling user input from the User Interfaces.
3
4Copyright (c) 2004 - 2012, Intel Corporation. All rights reserved.<BR>
5This program and the accompanying materials
6are licensed and made available under the terms and conditions of the BSD License
7which accompanies this distribution. The full text of the license may be found at
8http://opensource.org/licenses/bsd-license.php
9
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11WITHOUT 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**/
25VOID
26GetFieldFromOp (
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**/
59EFI_STATUS
60ReadString (
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**/
314VOID
315AdjustQuestionValue (
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**/
377VOID
378GetValueFromNum (
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**/
437EFI_STATUS
438GetNumericInput (
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
689TheKey2:
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
805EnterCarriageReturn:
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**/
968EFI_STATUS
969AdjustOptionOrder (
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**/
1037BOOLEAN
1038IsValuesEqual (
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**/
1072VOID
1073SetValuesByType (
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**/
1115EFI_STATUS
1116GetSelectionInputPopUp (
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
1354TheKey:
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