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