]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
MdeModulePkg: Clean up source files
[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 - 2018, 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
233 case CHAR_BACKSPACE:
234 if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {
235 for (Index = 0; Index < CurrentCursor - 1; Index++) {
236 TempString[Index] = StringPtr[Index];
237 }
238 Count = GetStringWidth (StringPtr) / 2 - 1;
239 if (Count >= CurrentCursor) {
240 for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {
241 TempString[Index] = StringPtr[Index2];
242 }
243 TempString[Index] = CHAR_NULL;
244 }
245 //
246 // Effectively truncate string by 1 character
247 //
248 StrCpyS (StringPtr, MaxLen, TempString);
249 CurrentCursor --;
250 }
251
252 default:
253 //
254 // If it is the beginning of the string, don't worry about checking maximum limits
255 //
256 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
257 StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1);
258 CurrentCursor++;
259 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
260 KeyPad[0] = Key.UnicodeChar;
261 KeyPad[1] = CHAR_NULL;
262 Count = GetStringWidth (StringPtr) / 2 - 1;
263 if (CurrentCursor < Count) {
264 for (Index = 0; Index < CurrentCursor; Index++) {
265 TempString[Index] = StringPtr[Index];
266 }
267 TempString[Index] = CHAR_NULL;
268 StrCatS (TempString, MaxLen, KeyPad);
269 StrCatS (TempString, MaxLen, StringPtr + CurrentCursor);
270 StrCpyS (StringPtr, MaxLen, TempString);
271 } else {
272 StrCatS (StringPtr, MaxLen, KeyPad);
273 }
274 CurrentCursor++;
275 }
276
277 //
278 // If the width of the input string is now larger than the screen, we nee to
279 // adjust the index to start printing portions of the string
280 //
281 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');
282 PrintStringAt (Start + 1, Top + 3, BufferedString);
283
284 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {
285 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;
286 } else {
287 Index = 0;
288 }
289
290 if (IsPassword) {
291 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);
292 }
293
294 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {
295 BufferedString[Count] = StringPtr[Index];
296
297 if (IsPassword) {
298 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');
299 }
300 }
301
302 if (!IsPassword) {
303 PrintStringAt (Start + 1, Top + 3, BufferedString);
304 }
305 break;
306 }
307
308 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
309 gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);
310 } while (TRUE);
311
312 }
313
314 /**
315 Adjust the value to the correct one. Rules follow the sample:
316 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01
317 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28
318
319 @param QuestionValue Pointer to current question.
320 @param Sequence The sequence of the field in the question.
321 **/
322 VOID
323 AdjustQuestionValue (
324 IN EFI_HII_VALUE *QuestionValue,
325 IN UINT8 Sequence
326 )
327 {
328 UINT8 Month;
329 UINT16 Year;
330 UINT8 Maximum;
331 UINT8 Minimum;
332
333 Month = QuestionValue->Value.date.Month;
334 Year = QuestionValue->Value.date.Year;
335 Minimum = 1;
336
337 switch (Month) {
338 case 2:
339 if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {
340 Maximum = 29;
341 } else {
342 Maximum = 28;
343 }
344 break;
345 case 4:
346 case 6:
347 case 9:
348 case 11:
349 Maximum = 30;
350 break;
351 default:
352 Maximum = 31;
353 break;
354 }
355
356 //
357 // Change the month area.
358 //
359 if (Sequence == 0) {
360 if (QuestionValue->Value.date.Day > Maximum) {
361 QuestionValue->Value.date.Day = Maximum;
362 }
363 }
364
365 //
366 // Change the Year area.
367 //
368 if (Sequence == 2) {
369 if (QuestionValue->Value.date.Day > Maximum) {
370 QuestionValue->Value.date.Day = Minimum;
371 }
372 }
373 }
374
375 /**
376 Get field info from numeric opcode.
377
378 @param OpCode Pointer to the current input opcode.
379 @param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type.
380 @param QuestionValue Input question value, with EFI_HII_VALUE type.
381 @param Value Return question value, always return UINT64 type.
382 @param Minimum The minimum size info for this opcode.
383 @param Maximum The maximum size info for this opcode.
384 @param Step The step size info for this opcode.
385 @param StorageWidth The storage width info for this opcode.
386
387 **/
388 VOID
389 GetValueFromNum (
390 IN EFI_IFR_OP_HEADER *OpCode,
391 IN BOOLEAN IntInput,
392 IN EFI_HII_VALUE *QuestionValue,
393 OUT UINT64 *Value,
394 OUT UINT64 *Minimum,
395 OUT UINT64 *Maximum,
396 OUT UINT64 *Step,
397 OUT UINT16 *StorageWidth
398 )
399 {
400 EFI_IFR_NUMERIC *NumericOp;
401
402 NumericOp = (EFI_IFR_NUMERIC *) OpCode;
403
404 switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {
405 case EFI_IFR_NUMERIC_SIZE_1:
406 if (IntInput) {
407 *Minimum = (INT64) (INT8) NumericOp->data.u8.MinValue;
408 *Maximum = (INT64) (INT8) NumericOp->data.u8.MaxValue;
409 *Value = (INT64) (INT8) QuestionValue->Value.u8;
410 } else {
411 *Minimum = NumericOp->data.u8.MinValue;
412 *Maximum = NumericOp->data.u8.MaxValue;
413 *Value = QuestionValue->Value.u8;
414 }
415 *Step = NumericOp->data.u8.Step;
416 *StorageWidth = (UINT16) sizeof (UINT8);
417 break;
418
419 case EFI_IFR_NUMERIC_SIZE_2:
420 if (IntInput) {
421 *Minimum = (INT64) (INT16) NumericOp->data.u16.MinValue;
422 *Maximum = (INT64) (INT16) NumericOp->data.u16.MaxValue;
423 *Value = (INT64) (INT16) QuestionValue->Value.u16;
424 } else {
425 *Minimum = NumericOp->data.u16.MinValue;
426 *Maximum = NumericOp->data.u16.MaxValue;
427 *Value = QuestionValue->Value.u16;
428 }
429 *Step = NumericOp->data.u16.Step;
430 *StorageWidth = (UINT16) sizeof (UINT16);
431 break;
432
433 case EFI_IFR_NUMERIC_SIZE_4:
434 if (IntInput) {
435 *Minimum = (INT64) (INT32) NumericOp->data.u32.MinValue;
436 *Maximum = (INT64) (INT32) NumericOp->data.u32.MaxValue;
437 *Value = (INT64) (INT32) QuestionValue->Value.u32;
438 } else {
439 *Minimum = NumericOp->data.u32.MinValue;
440 *Maximum = NumericOp->data.u32.MaxValue;
441 *Value = QuestionValue->Value.u32;
442 }
443 *Step = NumericOp->data.u32.Step;
444 *StorageWidth = (UINT16) sizeof (UINT32);
445 break;
446
447 case EFI_IFR_NUMERIC_SIZE_8:
448 if (IntInput) {
449 *Minimum = (INT64) NumericOp->data.u64.MinValue;
450 *Maximum = (INT64) NumericOp->data.u64.MaxValue;
451 *Value = (INT64) QuestionValue->Value.u64;
452 } else {
453 *Minimum = NumericOp->data.u64.MinValue;
454 *Maximum = NumericOp->data.u64.MaxValue;
455 *Value = QuestionValue->Value.u64;
456 }
457 *Step = NumericOp->data.u64.Step;
458 *StorageWidth = (UINT16) sizeof (UINT64);
459 break;
460
461 default:
462 break;
463 }
464
465 if (*Maximum == 0) {
466 *Maximum = (UINT64) -1;
467 }
468 }
469
470 /**
471 This routine reads a numeric value from the user input.
472
473 @param MenuOption Pointer to the current input menu.
474
475 @retval EFI_SUCCESS If numerical input is read successfully
476 @retval EFI_DEVICE_ERROR If operation fails
477
478 **/
479 EFI_STATUS
480 GetNumericInput (
481 IN UI_MENU_OPTION *MenuOption
482 )
483 {
484 UINTN Column;
485 UINTN Row;
486 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];
487 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];
488 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];
489 UINTN Count;
490 UINTN Loop;
491 BOOLEAN ManualInput;
492 BOOLEAN HexInput;
493 BOOLEAN IntInput;
494 BOOLEAN Negative;
495 BOOLEAN ValidateFail;
496 BOOLEAN DateOrTime;
497 UINTN InputWidth;
498 UINT64 EditValue;
499 UINT64 Step;
500 UINT64 Minimum;
501 UINT64 Maximum;
502 UINTN EraseLen;
503 UINT8 Digital;
504 EFI_INPUT_KEY Key;
505 EFI_HII_VALUE *QuestionValue;
506 FORM_DISPLAY_ENGINE_STATEMENT *Question;
507 EFI_IFR_NUMERIC *NumericOp;
508 UINT16 StorageWidth;
509
510 Column = MenuOption->OptCol;
511 Row = MenuOption->Row;
512 PreviousNumber[0] = 0;
513 Count = 0;
514 InputWidth = 0;
515 Digital = 0;
516 StorageWidth = 0;
517 Minimum = 0;
518 Maximum = 0;
519 NumericOp = NULL;
520 IntInput = FALSE;
521 HexInput = FALSE;
522 Negative = FALSE;
523 ValidateFail = FALSE;
524
525 Question = MenuOption->ThisTag;
526 QuestionValue = &Question->CurrentValue;
527 ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16));
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 InputText[InputWidth + 1] = DATE_SEPARATOR;
695 InputText[InputWidth + 2] = L'\0';
696 } else if (MenuOption->Sequence == 1){
697 SetUnicodeMem (InputText, InputWidth, L' ');
698 InputText[InputWidth] = DATE_SEPARATOR;
699 InputText[InputWidth + 1] = L'\0';
700 } else {
701 SetUnicodeMem (InputText, InputWidth, L' ');
702 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
703 InputText[InputWidth + 1] = L'\0';
704 }
705
706 PrintStringAt (Column, Row, InputText);
707 if (MenuOption->Sequence == 0) {
708 Column++;
709 }
710 }
711
712 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
713 InputWidth = 2;
714
715 if (MenuOption->Sequence == 0) {
716 InputText[0] = LEFT_NUMERIC_DELIMITER;
717 SetUnicodeMem (InputText + 1, InputWidth, L' ');
718 InputText[InputWidth + 1] = TIME_SEPARATOR;
719 InputText[InputWidth + 2] = L'\0';
720 } else if (MenuOption->Sequence == 1){
721 SetUnicodeMem (InputText, InputWidth, L' ');
722 InputText[InputWidth] = TIME_SEPARATOR;
723 InputText[InputWidth + 1] = L'\0';
724 } else {
725 SetUnicodeMem (InputText, InputWidth, L' ');
726 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;
727 InputText[InputWidth + 1] = L'\0';
728 }
729
730 PrintStringAt (Column, Row, InputText);
731 if (MenuOption->Sequence == 0) {
732 Column++;
733 }
734 }
735 }
736
737 //
738 // First time we enter this handler, we need to check to see if
739 // we were passed an increment or decrement directive
740 //
741 do {
742 Key.UnicodeChar = CHAR_NULL;
743 if (gDirection != 0) {
744 Key.ScanCode = gDirection;
745 gDirection = 0;
746 goto TheKey2;
747 }
748
749 WaitForKeyStroke (&Key);
750
751 TheKey2:
752 switch (Key.UnicodeChar) {
753
754 case '+':
755 case '-':
756 if (ManualInput && IntInput) {
757 //
758 // In Manual input mode, check whether input the negative flag.
759 //
760 if (Key.UnicodeChar == '-') {
761 if (Negative) {
762 break;
763 }
764 Negative = TRUE;
765 PrintCharAt (Column++, Row, Key.UnicodeChar);
766 }
767 } else {
768 if (Key.UnicodeChar == '+') {
769 Key.ScanCode = SCAN_RIGHT;
770 } else {
771 Key.ScanCode = SCAN_LEFT;
772 }
773 Key.UnicodeChar = CHAR_NULL;
774 goto TheKey2;
775 }
776 break;
777
778 case CHAR_NULL:
779 switch (Key.ScanCode) {
780 case SCAN_LEFT:
781 case SCAN_RIGHT:
782 if (DateOrTime && !ManualInput) {
783 //
784 // By setting this value, we will return back to the caller.
785 // We need to do this since an auto-refresh will destroy the adjustment
786 // based on what the real-time-clock is showing. So we always commit
787 // upon changing the value.
788 //
789 gDirection = SCAN_DOWN;
790 }
791
792 if ((Step != 0) && !ManualInput) {
793 if (Key.ScanCode == SCAN_LEFT) {
794 if (IntInput) {
795 if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {
796 EditValue = EditValue - Step;
797 } else if ((INT64) EditValue > (INT64) Minimum){
798 EditValue = Minimum;
799 } else {
800 EditValue = Maximum;
801 }
802 } else {
803 if (EditValue >= Minimum + Step) {
804 EditValue = EditValue - Step;
805 } else if (EditValue > Minimum){
806 EditValue = Minimum;
807 } else {
808 EditValue = Maximum;
809 }
810 }
811 } else if (Key.ScanCode == SCAN_RIGHT) {
812 if (IntInput) {
813 if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {
814 EditValue = EditValue + Step;
815 } else if ((INT64) EditValue < (INT64) Maximum) {
816 EditValue = Maximum;
817 } else {
818 EditValue = Minimum;
819 }
820 } else {
821 if (EditValue + Step <= Maximum) {
822 EditValue = EditValue + Step;
823 } else if (EditValue < Maximum) {
824 EditValue = Maximum;
825 } else {
826 EditValue = Minimum;
827 }
828 }
829 }
830
831 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
832 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
833 if (MenuOption->Sequence == 2) {
834 //
835 // Year
836 //
837 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
838 } else {
839 //
840 // Month/Day
841 //
842 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
843 }
844
845 if (MenuOption->Sequence == 0) {
846 ASSERT (EraseLen >= 2);
847 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
848 } else if (MenuOption->Sequence == 1) {
849 ASSERT (EraseLen >= 1);
850 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
851 }
852 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
853 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
854
855 if (MenuOption->Sequence == 0) {
856 ASSERT (EraseLen >= 2);
857 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
858 } else if (MenuOption->Sequence == 1) {
859 ASSERT (EraseLen >= 1);
860 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
861 }
862 } else {
863 QuestionValue->Value.u64 = EditValue;
864 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
865 }
866
867 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
868 for (Loop = 0; Loop < EraseLen; Loop++) {
869 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
870 }
871 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
872
873 if (MenuOption->Sequence == 0) {
874 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
875 Column = MenuOption->OptCol + 1;
876 }
877
878 PrintStringAt (Column, Row, FormattedNumber);
879
880 if (!DateOrTime || MenuOption->Sequence == 2) {
881 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
882 }
883 }
884
885 goto EnterCarriageReturn;
886
887 case SCAN_UP:
888 case SCAN_DOWN:
889 goto EnterCarriageReturn;
890
891 case SCAN_ESC:
892 return EFI_DEVICE_ERROR;
893
894 default:
895 break;
896 }
897
898 break;
899
900 EnterCarriageReturn:
901
902 case CHAR_CARRIAGE_RETURN:
903 //
904 // Validate input value with Minimum value.
905 //
906 ValidateFail = FALSE;
907 if (IntInput) {
908 //
909 // After user input Enter, need to check whether the input value.
910 // If input a negative value, should compare with maximum value.
911 // else compare with the minimum value.
912 //
913 if (Negative) {
914 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
915 } else {
916 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
917 }
918
919 if (ValidateFail) {
920 UpdateStatusBar (INPUT_ERROR, TRUE);
921 break;
922 }
923 } else if (EditValue < Minimum) {
924 UpdateStatusBar (INPUT_ERROR, TRUE);
925 break;
926 }
927
928 UpdateStatusBar (INPUT_ERROR, FALSE);
929 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
930 QuestionValue = &gUserInput->InputValue;
931 //
932 // Store Edit value back to Question
933 //
934 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
935 switch (MenuOption->Sequence) {
936 case 0:
937 QuestionValue->Value.date.Month = (UINT8) EditValue;
938 break;
939
940 case 1:
941 QuestionValue->Value.date.Day = (UINT8) EditValue;
942 break;
943
944 case 2:
945 QuestionValue->Value.date.Year = (UINT16) EditValue;
946 break;
947
948 default:
949 break;
950 }
951 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
952 switch (MenuOption->Sequence) {
953 case 0:
954 QuestionValue->Value.time.Hour = (UINT8) EditValue;
955 break;
956
957 case 1:
958 QuestionValue->Value.time.Minute = (UINT8) EditValue;
959 break;
960
961 case 2:
962 QuestionValue->Value.time.Second = (UINT8) EditValue;
963 break;
964
965 default:
966 break;
967 }
968 } else {
969 //
970 // Numeric
971 //
972 QuestionValue->Value.u64 = EditValue;
973 }
974
975 //
976 // Adjust the value to the correct one.
977 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
978 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
979 //
980 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP &&
981 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
982 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
983 }
984
985 return EFI_SUCCESS;
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 < ARRAY_SIZE (PreviousNumber));
1077 EditValue = PreviousNumber[Count];
1078 break;
1079 }
1080 } else {
1081 if (EditValue > Maximum) {
1082 UpdateStatusBar (INPUT_ERROR, TRUE);
1083 ASSERT (Count < ARRAY_SIZE (PreviousNumber));
1084 EditValue = PreviousNumber[Count];
1085 break;
1086 }
1087 }
1088
1089 UpdateStatusBar (INPUT_ERROR, FALSE);
1090
1091 Count++;
1092 ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));
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 ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));
1306
1307 Question = MenuOption->ThisTag;
1308 if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {
1309 Link = GetFirstNode (&Question->OptionListHead);
1310 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1311 ValueArray = Question->CurrentValue.Buffer;
1312 ValueType = OneOfOption->OptionOpCode->Type;
1313 OrderedList = TRUE;
1314 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
1315 } else {
1316 OrderedList = FALSE;
1317 OrderList = NULL;
1318 }
1319
1320 //
1321 // Calculate Option count
1322 //
1323 PopUpMenuLines = 0;
1324 if (OrderedList) {
1325 AdjustOptionOrder(Question, &PopUpMenuLines);
1326 } else {
1327 Link = GetFirstNode (&Question->OptionListHead);
1328 while (!IsNull (&Question->OptionListHead, Link)) {
1329 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1330 PopUpMenuLines++;
1331 Link = GetNextNode (&Question->OptionListHead, Link);
1332 }
1333 }
1334
1335 //
1336 // Get the number of one of options present and its size
1337 //
1338 PopUpWidth = 0;
1339 HighlightOptionIndex = 0;
1340 Link = GetFirstNode (&Question->OptionListHead);
1341 for (Index = 0; Index < PopUpMenuLines; Index++) {
1342 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1343
1344 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1345 if (StrLen (StringPtr) > PopUpWidth) {
1346 PopUpWidth = StrLen (StringPtr);
1347 }
1348 FreePool (StringPtr);
1349 HiiValue.Type = OneOfOption->OptionOpCode->Type;
1350 SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);
1351 if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {
1352 //
1353 // Find current selected Option for OneOf
1354 //
1355 HighlightOptionIndex = Index;
1356 }
1357
1358 Link = GetNextNode (&Question->OptionListHead, Link);
1359 }
1360
1361 //
1362 // Perform popup menu initialization.
1363 //
1364 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;
1365
1366 SavedAttribute = gST->ConOut->Mode->Attribute;
1367 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1368
1369 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {
1370 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;
1371 }
1372
1373 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;
1374 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;
1375 Top = gStatementDimensions.TopRow;
1376 Bottom = gStatementDimensions.BottomRow - 1;
1377
1378 MenuLinesInView = Bottom - Top - 1;
1379 if (MenuLinesInView >= PopUpMenuLines) {
1380 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;
1381 Bottom = Top + PopUpMenuLines + 1;
1382 } else {
1383 ShowDownArrow = TRUE;
1384 }
1385
1386 if (HighlightOptionIndex > (MenuLinesInView - 1)) {
1387 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;
1388 } else {
1389 TopOptionIndex = 0;
1390 }
1391
1392 do {
1393 //
1394 // Clear that portion of the screen
1395 //
1396 ClearLines (Start, End, Top, Bottom, GetPopupColor ());
1397
1398 //
1399 // Draw "One of" pop-up menu
1400 //
1401 Character = BOXDRAW_DOWN_RIGHT;
1402 PrintCharAt (Start, Top, Character);
1403 for (Index = Start; Index + 2 < End; Index++) {
1404 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {
1405 Character = GEOMETRICSHAPE_UP_TRIANGLE;
1406 } else {
1407 Character = BOXDRAW_HORIZONTAL;
1408 }
1409
1410 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1411 }
1412
1413 Character = BOXDRAW_DOWN_LEFT;
1414 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1415 Character = BOXDRAW_VERTICAL;
1416 for (Index = Top + 1; Index < Bottom; Index++) {
1417 PrintCharAt (Start, Index, Character);
1418 PrintCharAt (End - 1, Index, Character);
1419 }
1420
1421 //
1422 // Move to top Option
1423 //
1424 Link = GetFirstNode (&Question->OptionListHead);
1425 for (Index = 0; Index < TopOptionIndex; Index++) {
1426 Link = GetNextNode (&Question->OptionListHead, Link);
1427 }
1428
1429 //
1430 // Display the One of options
1431 //
1432 Index2 = Top + 1;
1433 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {
1434 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1435 Link = GetNextNode (&Question->OptionListHead, Link);
1436
1437 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);
1438 ASSERT (StringPtr != NULL);
1439 //
1440 // If the string occupies multiple lines, truncate it to fit in one line,
1441 // and append a "..." for indication.
1442 //
1443 if (StrLen (StringPtr) > (PopUpWidth - 1)) {
1444 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));
1445 ASSERT ( TempStringPtr != NULL );
1446 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));
1447 FreePool (StringPtr);
1448 StringPtr = TempStringPtr;
1449 StrCatS (StringPtr, PopUpWidth - 1, L"...");
1450 }
1451
1452 if (Index == HighlightOptionIndex) {
1453 //
1454 // Highlight the selected one
1455 //
1456 CurrentOption = OneOfOption;
1457
1458 gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());
1459 PrintStringAt (Start + 2, Index2, StringPtr);
1460 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1461 } else {
1462 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());
1463 PrintStringAt (Start + 2, Index2, StringPtr);
1464 }
1465
1466 Index2++;
1467 FreePool (StringPtr);
1468 }
1469
1470 Character = BOXDRAW_UP_RIGHT;
1471 PrintCharAt (Start, Bottom, Character);
1472 for (Index = Start; Index + 2 < End; Index++) {
1473 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {
1474 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;
1475 } else {
1476 Character = BOXDRAW_HORIZONTAL;
1477 }
1478
1479 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1480 }
1481
1482 Character = BOXDRAW_UP_LEFT;
1483 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);
1484
1485 //
1486 // Get User selection
1487 //
1488 Key.UnicodeChar = CHAR_NULL;
1489 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {
1490 Key.ScanCode = gDirection;
1491 gDirection = 0;
1492 goto TheKey;
1493 }
1494
1495 WaitForKeyStroke (&Key);
1496
1497 TheKey:
1498 switch (Key.UnicodeChar) {
1499 case '+':
1500 if (OrderedList) {
1501 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1502 //
1503 // Highlight reaches the top of the popup window, scroll one menu item.
1504 //
1505 TopOptionIndex--;
1506 ShowDownArrow = TRUE;
1507 }
1508
1509 if (TopOptionIndex == 0) {
1510 ShowUpArrow = FALSE;
1511 }
1512
1513 if (HighlightOptionIndex > 0) {
1514 HighlightOptionIndex--;
1515
1516 ASSERT (CurrentOption != NULL);
1517 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);
1518 }
1519 }
1520 break;
1521
1522 case '-':
1523 //
1524 // If an ordered list op-code, we will allow for a popup of +/- keys
1525 // to create an ordered list of items
1526 //
1527 if (OrderedList) {
1528 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1529 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1530 //
1531 // Highlight reaches the bottom of the popup window, scroll one menu item.
1532 //
1533 TopOptionIndex++;
1534 ShowUpArrow = TRUE;
1535 }
1536
1537 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1538 ShowDownArrow = FALSE;
1539 }
1540
1541 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1542 HighlightOptionIndex++;
1543
1544 ASSERT (CurrentOption != NULL);
1545 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);
1546 }
1547 }
1548 break;
1549
1550 case CHAR_NULL:
1551 switch (Key.ScanCode) {
1552 case SCAN_UP:
1553 case SCAN_DOWN:
1554 if (Key.ScanCode == SCAN_UP) {
1555 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {
1556 //
1557 // Highlight reaches the top of the popup window, scroll one menu item.
1558 //
1559 TopOptionIndex--;
1560 ShowDownArrow = TRUE;
1561 }
1562
1563 if (TopOptionIndex == 0) {
1564 ShowUpArrow = FALSE;
1565 }
1566
1567 if (HighlightOptionIndex > 0) {
1568 HighlightOptionIndex--;
1569 }
1570 } else {
1571 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&
1572 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {
1573 //
1574 // Highlight reaches the bottom of the popup window, scroll one menu item.
1575 //
1576 TopOptionIndex++;
1577 ShowUpArrow = TRUE;
1578 }
1579
1580 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {
1581 ShowDownArrow = FALSE;
1582 }
1583
1584 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {
1585 HighlightOptionIndex++;
1586 }
1587 }
1588 break;
1589
1590 case SCAN_ESC:
1591 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1592
1593 //
1594 // Restore link list order for orderedlist
1595 //
1596 if (OrderedList) {
1597 HiiValue.Type = ValueType;
1598 HiiValue.Value.u64 = 0;
1599 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1600 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1601 if (HiiValue.Value.u64 == 0) {
1602 break;
1603 }
1604
1605 OneOfOption = ValueToOption (Question, &HiiValue);
1606 if (OneOfOption == NULL) {
1607 return EFI_NOT_FOUND;
1608 }
1609
1610 RemoveEntryList (&OneOfOption->Link);
1611 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);
1612 }
1613 }
1614
1615 return EFI_DEVICE_ERROR;
1616
1617 default:
1618 break;
1619 }
1620
1621 break;
1622
1623 case CHAR_CARRIAGE_RETURN:
1624 //
1625 // return the current selection
1626 //
1627 if (OrderedList) {
1628 ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);
1629 ASSERT (ReturnValue != NULL);
1630 Index = 0;
1631 Link = GetFirstNode (&Question->OptionListHead);
1632 while (!IsNull (&Question->OptionListHead, Link)) {
1633 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1634 Link = GetNextNode (&Question->OptionListHead, Link);
1635
1636 SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);
1637
1638 Index++;
1639 if (Index > OrderList->MaxContainers) {
1640 break;
1641 }
1642 }
1643 if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {
1644 FreePool (ReturnValue);
1645 return EFI_DEVICE_ERROR;
1646 } else {
1647 gUserInput->InputValue.Buffer = ReturnValue;
1648 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;
1649 }
1650 } else {
1651 ASSERT (CurrentOption != NULL);
1652 gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;
1653 if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {
1654 return EFI_DEVICE_ERROR;
1655 } else {
1656 SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);
1657 }
1658 }
1659
1660 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);
1661
1662 return EFI_SUCCESS;
1663
1664 default:
1665 break;
1666 }
1667 } while (TRUE);
1668
1669 }
1670