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