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