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