]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
MdeModulePkg: remove unreachable code
[mirror_edk2.git] / MdeModulePkg / Universal / DisplayEngineDxe / InputHandler.c
1 /** @file
2 Implementation for handling user input from the User Interfaces.
3
4 Copyright (c) 2004 - 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
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
528 //
529 // Only two case, user can enter to this function: Enter and +/- case.
530 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT
531 //
532 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);
533
534 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {
535 DateOrTime = TRUE;
536 } else {
537 DateOrTime = FALSE;
538 }
539
540 //
541 // Prepare Value to be edit
542 //
543 EraseLen = 0;
544 EditValue = 0;
545 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
546 Step = 1;
547 Minimum = 1;
548
549 switch (MenuOption->Sequence) {
550 case 0:
551 Maximum = 12;
552 EraseLen = 4;
553 EditValue = QuestionValue->Value.date.Month;
554 break;
555
556 case 1:
557 switch (QuestionValue->Value.date.Month) {
558 case 2:
559 if ((QuestionValue->Value.date.Year % 4) == 0 &&
560 ((QuestionValue->Value.date.Year % 100) != 0 ||
561 (QuestionValue->Value.date.Year % 400) == 0)) {
562 Maximum = 29;
563 } else {
564 Maximum = 28;
565 }
566 break;
567 case 4:
568 case 6:
569 case 9:
570 case 11:
571 Maximum = 30;
572 break;
573 default:
574 Maximum = 31;
575 break;
576 }
577
578 EraseLen = 3;
579 EditValue = QuestionValue->Value.date.Day;
580 break;
581
582 case 2:
583 Maximum = 0xffff;
584 EraseLen = 5;
585 EditValue = QuestionValue->Value.date.Year;
586 break;
587
588 default:
589 break;
590 }
591 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
592 Step = 1;
593 Minimum = 0;
594
595 switch (MenuOption->Sequence) {
596 case 0:
597 Maximum = 23;
598 EraseLen = 4;
599 EditValue = QuestionValue->Value.time.Hour;
600 break;
601
602 case 1:
603 Maximum = 59;
604 EraseLen = 3;
605 EditValue = QuestionValue->Value.time.Minute;
606 break;
607
608 case 2:
609 Maximum = 59;
610 EraseLen = 3;
611 EditValue = QuestionValue->Value.time.Second;
612 break;
613
614 default:
615 break;
616 }
617 } else {
618 ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);
619 NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;
620 GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);
621 EraseLen = gOptionBlockWidth;
622 }
623
624 if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {
625 if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){
626 HexInput = TRUE;
627 } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){
628 //
629 // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.
630 //
631 IntInput = TRUE;
632 }
633 }
634
635 //
636 // Enter from "Enter" input, clear the old word showing.
637 //
638 if (ManualInput) {
639 if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {
640 if (HexInput) {
641 InputWidth = StorageWidth * 2;
642 } else {
643 switch (StorageWidth) {
644 case 1:
645 InputWidth = 3;
646 break;
647
648 case 2:
649 InputWidth = 5;
650 break;
651
652 case 4:
653 InputWidth = 10;
654 break;
655
656 case 8:
657 InputWidth = 20;
658 break;
659
660 default:
661 InputWidth = 0;
662 break;
663 }
664
665 if (IntInput) {
666 //
667 // Support an extra '-' for negative number.
668 //
669 InputWidth += 1;
670 }
671 }
672
673 InputText[0] = LEFT_NUMERIC_DELIMITER;
674 SetUnicodeMem (InputText + 1, InputWidth, L' ');
675 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);
676 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
677 InputText[InputWidth + 2] = L'\0';
678
679 PrintStringAt (Column, Row, InputText);
680 Column++;
681 }
682
683 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
684 if (MenuOption->Sequence == 2) {
685 InputWidth = 4;
686 } else {
687 InputWidth = 2;
688 }
689
690 if (MenuOption->Sequence == 0) {
691 InputText[0] = LEFT_NUMERIC_DELIMITER;
692 SetUnicodeMem (InputText + 1, InputWidth, L' ');
693 } else {
694 SetUnicodeMem (InputText, InputWidth, L' ');
695 }
696
697 if (MenuOption->Sequence == 2) {
698 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
699 } else {
700 InputText[InputWidth + 1] = DATE_SEPARATOR;
701 }
702 InputText[InputWidth + 2] = L'\0';
703
704 PrintStringAt (Column, Row, InputText);
705 if (MenuOption->Sequence == 0) {
706 Column++;
707 }
708 }
709
710 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
711 InputWidth = 2;
712
713 if (MenuOption->Sequence == 0) {
714 InputText[0] = LEFT_NUMERIC_DELIMITER;
715 SetUnicodeMem (InputText + 1, InputWidth, L' ');
716 } else {
717 SetUnicodeMem (InputText, InputWidth, L' ');
718 }
719
720 if (MenuOption->Sequence == 2) {
721 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;
722 } else {
723 InputText[InputWidth + 1] = TIME_SEPARATOR;
724 }
725 InputText[InputWidth + 2] = L'\0';
726
727 PrintStringAt (Column, Row, InputText);
728 if (MenuOption->Sequence == 0) {
729 Column++;
730 }
731 }
732 }
733
734 //
735 // First time we enter this handler, we need to check to see if
736 // we were passed an increment or decrement directive
737 //
738 do {
739 Key.UnicodeChar = CHAR_NULL;
740 if (gDirection != 0) {
741 Key.ScanCode = gDirection;
742 gDirection = 0;
743 goto TheKey2;
744 }
745
746 WaitForKeyStroke (&Key);
747
748 TheKey2:
749 switch (Key.UnicodeChar) {
750
751 case '+':
752 case '-':
753 if (ManualInput && IntInput) {
754 //
755 // In Manual input mode, check whether input the negative flag.
756 //
757 if (Key.UnicodeChar == '-') {
758 if (Negative) {
759 break;
760 }
761 Negative = TRUE;
762 PrintCharAt (Column++, Row, Key.UnicodeChar);
763 }
764 } else {
765 if (Key.UnicodeChar == '+') {
766 Key.ScanCode = SCAN_RIGHT;
767 } else {
768 Key.ScanCode = SCAN_LEFT;
769 }
770 Key.UnicodeChar = CHAR_NULL;
771 goto TheKey2;
772 }
773 break;
774
775 case CHAR_NULL:
776 switch (Key.ScanCode) {
777 case SCAN_LEFT:
778 case SCAN_RIGHT:
779 if (DateOrTime && !ManualInput) {
780 //
781 // By setting this value, we will return back to the caller.
782 // We need to do this since an auto-refresh will destroy the adjustment
783 // based on what the real-time-clock is showing. So we always commit
784 // upon changing the value.
785 //
786 gDirection = SCAN_DOWN;
787 }
788
789 if ((Step != 0) && !ManualInput) {
790 if (Key.ScanCode == SCAN_LEFT) {
791 if (IntInput) {
792 if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {
793 EditValue = EditValue - Step;
794 } else if ((INT64) EditValue > (INT64) Minimum){
795 EditValue = Minimum;
796 } else {
797 EditValue = Maximum;
798 }
799 } else {
800 if (EditValue >= Minimum + Step) {
801 EditValue = EditValue - Step;
802 } else if (EditValue > Minimum){
803 EditValue = Minimum;
804 } else {
805 EditValue = Maximum;
806 }
807 }
808 } else if (Key.ScanCode == SCAN_RIGHT) {
809 if (IntInput) {
810 if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {
811 EditValue = EditValue + Step;
812 } else if ((INT64) EditValue < (INT64) Maximum) {
813 EditValue = Maximum;
814 } else {
815 EditValue = Minimum;
816 }
817 } else {
818 if (EditValue + Step <= Maximum) {
819 EditValue = EditValue + Step;
820 } else if (EditValue < Maximum) {
821 EditValue = Maximum;
822 } else {
823 EditValue = Minimum;
824 }
825 }
826 }
827
828 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));
829 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
830 if (MenuOption->Sequence == 2) {
831 //
832 // Year
833 //
834 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);
835 } else {
836 //
837 // Month/Day
838 //
839 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
840 }
841
842 if (MenuOption->Sequence == 0) {
843 ASSERT (EraseLen >= 2);
844 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;
845 } else if (MenuOption->Sequence == 1) {
846 ASSERT (EraseLen >= 1);
847 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;
848 }
849 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
850 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);
851
852 if (MenuOption->Sequence == 0) {
853 ASSERT (EraseLen >= 2);
854 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;
855 } else if (MenuOption->Sequence == 1) {
856 ASSERT (EraseLen >= 1);
857 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;
858 }
859 } else {
860 QuestionValue->Value.u64 = EditValue;
861 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));
862 }
863
864 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());
865 for (Loop = 0; Loop < EraseLen; Loop++) {
866 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");
867 }
868 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
869
870 if (MenuOption->Sequence == 0) {
871 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);
872 Column = MenuOption->OptCol + 1;
873 }
874
875 PrintStringAt (Column, Row, FormattedNumber);
876
877 if (!DateOrTime || MenuOption->Sequence == 2) {
878 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);
879 }
880 }
881
882 goto EnterCarriageReturn;
883
884 case SCAN_UP:
885 case SCAN_DOWN:
886 goto EnterCarriageReturn;
887
888 case SCAN_ESC:
889 return EFI_DEVICE_ERROR;
890
891 default:
892 break;
893 }
894
895 break;
896
897 EnterCarriageReturn:
898
899 case CHAR_CARRIAGE_RETURN:
900 //
901 // Validate input value with Minimum value.
902 //
903 ValidateFail = FALSE;
904 if (IntInput) {
905 //
906 // After user input Enter, need to check whether the input value.
907 // If input a negative value, should compare with maximum value.
908 // else compare with the minimum value.
909 //
910 if (Negative) {
911 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
912 } else {
913 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
914 }
915
916 if (ValidateFail) {
917 UpdateStatusBar (INPUT_ERROR, TRUE);
918 break;
919 }
920 } else if (EditValue < Minimum) {
921 UpdateStatusBar (INPUT_ERROR, TRUE);
922 break;
923 }
924
925 UpdateStatusBar (INPUT_ERROR, FALSE);
926 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));
927 QuestionValue = &gUserInput->InputValue;
928 //
929 // Store Edit value back to Question
930 //
931 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {
932 switch (MenuOption->Sequence) {
933 case 0:
934 QuestionValue->Value.date.Month = (UINT8) EditValue;
935 break;
936
937 case 1:
938 QuestionValue->Value.date.Day = (UINT8) EditValue;
939 break;
940
941 case 2:
942 QuestionValue->Value.date.Year = (UINT16) EditValue;
943 break;
944
945 default:
946 break;
947 }
948 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {
949 switch (MenuOption->Sequence) {
950 case 0:
951 QuestionValue->Value.time.Hour = (UINT8) EditValue;
952 break;
953
954 case 1:
955 QuestionValue->Value.time.Minute = (UINT8) EditValue;
956 break;
957
958 case 2:
959 QuestionValue->Value.time.Second = (UINT8) EditValue;
960 break;
961
962 default:
963 break;
964 }
965 } else {
966 //
967 // Numeric
968 //
969 QuestionValue->Value.u64 = EditValue;
970 }
971
972 //
973 // Adjust the value to the correct one.
974 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01
975 // 2013.03.29 -> 2013.02.29 -> 2013.02.28
976 //
977 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP &&
978 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {
979 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);
980 }
981
982 return EFI_SUCCESS;
983
984 case CHAR_BACKSPACE:
985 if (ManualInput) {
986 if (Count == 0) {
987 if (Negative) {
988 Negative = FALSE;
989 Column--;
990 PrintStringAt (Column, Row, L" ");
991 }
992 break;
993 }
994 //
995 // Remove a character
996 //
997 EditValue = PreviousNumber[Count - 1];
998 UpdateStatusBar (INPUT_ERROR, FALSE);
999 Count--;
1000 Column--;
1001 PrintStringAt (Column, Row, L" ");
1002 }
1003 break;
1004
1005 default:
1006 if (ManualInput) {
1007 if (HexInput) {
1008 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {
1009 Digital = (UINT8) (Key.UnicodeChar - L'0');
1010 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {
1011 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);
1012 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {
1013 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);
1014 } else {
1015 UpdateStatusBar (INPUT_ERROR, TRUE);
1016 break;
1017 }
1018 } else {
1019 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {
1020 UpdateStatusBar (INPUT_ERROR, TRUE);
1021 break;
1022 }
1023 }
1024
1025 //
1026 // If Count exceed input width, there is no way more is valid
1027 //
1028 if (Count >= InputWidth) {
1029 break;
1030 }
1031 //
1032 // Someone typed something valid!
1033 //
1034 if (Count != 0) {
1035 if (HexInput) {
1036 EditValue = LShiftU64 (EditValue, 4) + Digital;
1037 } else if (IntInput && Negative) {
1038 //
1039 // Save the negative number.
1040 //
1041 EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;
1042 } else {
1043 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');
1044 }
1045 } else {
1046 if (HexInput) {
1047 EditValue = Digital;
1048 } else if (IntInput && Negative) {
1049 //
1050 // Save the negative number.
1051 //
1052 EditValue = ~(Key.UnicodeChar - L'0') + 1;
1053 } else {
1054 EditValue = Key.UnicodeChar - L'0';
1055 }
1056 }
1057
1058 if (IntInput) {
1059 ValidateFail = FALSE;
1060 //
1061 // When user input a new value, should check the current value.
1062 // If user input a negative value, should compare it with minimum
1063 // value, else compare it with maximum value.
1064 //
1065 if (Negative) {
1066 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;
1067 } else {
1068 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;
1069 }
1070
1071 if (ValidateFail) {
1072 UpdateStatusBar (INPUT_ERROR, TRUE);
1073 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
1074 EditValue = PreviousNumber[Count];
1075 break;
1076 }
1077 } else {
1078 if (EditValue > Maximum) {
1079 UpdateStatusBar (INPUT_ERROR, TRUE);
1080 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));
1081 EditValue = PreviousNumber[Count];
1082 break;
1083 }
1084 }
1085
1086 UpdateStatusBar (INPUT_ERROR, FALSE);
1087
1088 Count++;
1089 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));
1090 PreviousNumber[Count] = EditValue;
1091
1092 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());
1093 PrintCharAt (Column, Row, Key.UnicodeChar);
1094 Column++;
1095 }
1096 break;
1097 }
1098 } while (TRUE);
1099 }
1100
1101 /**
1102 Adjust option order base on the question value.
1103
1104 @param Question Pointer to current question.
1105 @param PopUpMenuLines The line number of the pop up menu.
1106
1107 @retval EFI_SUCCESS If Option input is processed successfully
1108 @retval EFI_DEVICE_ERROR If operation fails
1109
1110 **/
1111 EFI_STATUS
1112 AdjustOptionOrder (
1113 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,
1114 OUT UINTN *PopUpMenuLines
1115 )
1116 {
1117 UINTN Index;
1118 EFI_IFR_ORDERED_LIST *OrderList;
1119 UINT8 *ValueArray;
1120 UINT8 ValueType;
1121 LIST_ENTRY *Link;
1122 DISPLAY_QUESTION_OPTION *OneOfOption;
1123 EFI_HII_VALUE *HiiValueArray;
1124
1125 Link = GetFirstNode (&Question->OptionListHead);
1126 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);
1127 ValueArray = Question->CurrentValue.Buffer;
1128 ValueType = OneOfOption->OptionOpCode->Type;
1129 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;
1130
1131 for (Index = 0; Index < OrderList->MaxContainers; Index++) {
1132 if (GetArrayData (ValueArray, ValueType, Index) == 0) {
1133 break;
1134 }
1135 }
1136
1137 *PopUpMenuLines = Index;
1138
1139 //
1140 // Prepare HiiValue array
1141 //
1142 HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));
1143 ASSERT (HiiValueArray != NULL);
1144
1145 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1146 HiiValueArray[Index].Type = ValueType;
1147 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);
1148 }
1149
1150 for (Index = 0; Index < *PopUpMenuLines; Index++) {
1151 OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);
1152 if (OneOfOption == NULL) {
1153 return EFI_NOT_FOUND;
1154 }
1155
1156 RemoveEntryList (&OneOfOption->Link);
1157
1158 //
1159 // Insert to head.
1160 //
1161 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);
1162 }
1163
1164 FreePool (HiiValueArray);
1165
1166 return EFI_SUCCESS;
1167 }
1168
1169 /**
1170 Base on the type to compare the value.
1171
1172 @param Value1 The first value need to compare.
1173 @param Value2 The second value need to compare.
1174 @param Type The value type for above two values.
1175
1176 @retval TRUE The two value are same.
1177 @retval FALSE The two value are different.
1178
1179 **/
1180 BOOLEAN
1181 IsValuesEqual (
1182 IN EFI_IFR_TYPE_VALUE *Value1,
1183 IN EFI_IFR_TYPE_VALUE *Value2,
1184 IN UINT8 Type
1185 )
1186 {
1187 switch (Type) {
1188 case EFI_IFR_TYPE_BOOLEAN:
1189 case EFI_IFR_TYPE_NUM_SIZE_8:
1190 return (BOOLEAN) (Value1->u8 == Value2->u8);
1191
1192 case EFI_IFR_TYPE_NUM_SIZE_16:
1193 return (BOOLEAN) (Value1->u16 == Value2->u16);
1194
1195 case EFI_IFR_TYPE_NUM_SIZE_32:
1196 return (BOOLEAN) (Value1->u32 == Value2->u32);
1197
1198 case EFI_IFR_TYPE_NUM_SIZE_64:
1199 return (BOOLEAN) (Value1->u64 == Value2->u64);
1200
1201 default:
1202 ASSERT (FALSE);
1203 return FALSE;
1204 }
1205 }
1206
1207 /**
1208 Base on the type to set the value.
1209
1210 @param Dest The dest value.
1211 @param Source The source value.
1212 @param Type The value type for above two values.
1213
1214 **/
1215 VOID
1216 SetValuesByType (
1217 OUT EFI_IFR_TYPE_VALUE *Dest,
1218 IN EFI_IFR_TYPE_VALUE *Source,
1219 IN UINT8 Type
1220 )
1221 {
1222 switch (Type) {
1223 case EFI_IFR_TYPE_BOOLEAN:
1224 Dest->b = Source->b;
1225 break;
1226
1227 case EFI_IFR_TYPE_NUM_SIZE_8:
1228 Dest->u8 = Source->u8;
1229 break;
1230
1231 case EFI_IFR_TYPE_NUM_SIZE_16:
1232 Dest->u16 = Source->u16;
1233 break;
1234
1235 case EFI_IFR_TYPE_NUM_SIZE_32:
1236 Dest->u32 = Source->u32;
1237 break;
1238
1239 case EFI_IFR_TYPE_NUM_SIZE_64:
1240 Dest->u64 = Source->u64;
1241 break;
1242
1243 default:
1244 ASSERT (FALSE);
1245 break;
1246 }
1247 }
1248
1249 /**
1250 Get selection for OneOf and OrderedList (Left/Right will be ignored).
1251
1252 @param MenuOption Pointer to the current input menu.
1253
1254 @retval EFI_SUCCESS If Option input is processed successfully
1255 @retval EFI_DEVICE_ERROR If operation fails
1256
1257 **/
1258 EFI_STATUS
1259 GetSelectionInputPopUp (
1260 IN UI_MENU_OPTION *MenuOption
1261 )
1262 {
1263 EFI_INPUT_KEY Key;
1264 UINTN Index;
1265 CHAR16 *StringPtr;
1266 CHAR16 *TempStringPtr;
1267 UINTN Index2;
1268 UINTN TopOptionIndex;
1269 UINTN HighlightOptionIndex;
1270 UINTN Start;
1271 UINTN End;
1272 UINTN Top;
1273 UINTN Bottom;
1274 UINTN PopUpMenuLines;
1275 UINTN MenuLinesInView;
1276 UINTN PopUpWidth;
1277 CHAR16 Character;
1278 INT32 SavedAttribute;
1279 BOOLEAN ShowDownArrow;
1280 BOOLEAN ShowUpArrow;
1281 UINTN DimensionsWidth;
1282 LIST_ENTRY *Link;
1283 BOOLEAN OrderedList;
1284 UINT8 *ValueArray;
1285 UINT8 *ReturnValue;
1286 UINT8 ValueType;
1287 EFI_HII_VALUE HiiValue;
1288 DISPLAY_QUESTION_OPTION *OneOfOption;
1289 DISPLAY_QUESTION_OPTION *CurrentOption;
1290 FORM_DISPLAY_ENGINE_STATEMENT *Question;
1291 INTN Result;
1292 EFI_IFR_ORDERED_LIST *OrderList;
1293
1294 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;
1295
1296 ValueArray = NULL;
1297 ValueType = 0;
1298 CurrentOption = NULL;
1299 ShowDownArrow = FALSE;
1300 ShowUpArrow = FALSE;
1301
1302 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);
1303 ASSERT (StringPtr);
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