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