]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
MdeModulePkg: Clean up source files
[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
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
d1102dba 37 *Maximum = StringOp->MaxSize;\r
7c6c064c
ED
38 } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {\r
39 PasswordOp = (EFI_IFR_PASSWORD *) OpCode;\r
40 *Minimum = PasswordOp->MinSize;\r
d1102dba 41 *Maximum = PasswordOp->MaxSize;\r
7c6c064c
ED
42 } else {\r
43 *Minimum = 0;\r
d1102dba 44 *Maximum = 0;\r
7c6c064c
ED
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
d1102dba 163\r
7c6c064c
ED
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
d1102dba 167\r
7c6c064c
ED
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
7c6c064c
ED
232\r
233 case CHAR_BACKSPACE:\r
234 if (StringPtr[0] != CHAR_NULL && CurrentCursor != 0) {\r
235 for (Index = 0; Index < CurrentCursor - 1; Index++) {\r
236 TempString[Index] = StringPtr[Index];\r
237 }\r
238 Count = GetStringWidth (StringPtr) / 2 - 1;\r
239 if (Count >= CurrentCursor) {\r
240 for (Index = CurrentCursor - 1, Index2 = CurrentCursor; Index2 < Count; Index++, Index2++) {\r
241 TempString[Index] = StringPtr[Index2];\r
242 }\r
243 TempString[Index] = CHAR_NULL;\r
244 }\r
245 //\r
246 // Effectively truncate string by 1 character\r
247 //\r
5ad66ec6 248 StrCpyS (StringPtr, MaxLen, TempString);\r
7c6c064c
ED
249 CurrentCursor --;\r
250 }\r
251\r
252 default:\r
253 //\r
254 // If it is the beginning of the string, don't worry about checking maximum limits\r
255 //\r
256 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
5ad66ec6 257 StrnCpyS (StringPtr, MaxLen, &Key.UnicodeChar, 1);\r
7c6c064c
ED
258 CurrentCursor++;\r
259 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
260 KeyPad[0] = Key.UnicodeChar;\r
261 KeyPad[1] = CHAR_NULL;\r
262 Count = GetStringWidth (StringPtr) / 2 - 1;\r
263 if (CurrentCursor < Count) {\r
264 for (Index = 0; Index < CurrentCursor; Index++) {\r
265 TempString[Index] = StringPtr[Index];\r
266 }\r
d1102dba 267 TempString[Index] = CHAR_NULL;\r
5ad66ec6
DB
268 StrCatS (TempString, MaxLen, KeyPad);\r
269 StrCatS (TempString, MaxLen, StringPtr + CurrentCursor);\r
270 StrCpyS (StringPtr, MaxLen, TempString);\r
7c6c064c 271 } else {\r
5ad66ec6 272 StrCatS (StringPtr, MaxLen, KeyPad);\r
7c6c064c
ED
273 }\r
274 CurrentCursor++;\r
275 }\r
276\r
277 //\r
278 // If the width of the input string is now larger than the screen, we nee to\r
279 // adjust the index to start printing portions of the string\r
280 //\r
281 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');\r
282 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
283\r
284 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {\r
285 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;\r
286 } else {\r
287 Index = 0;\r
288 }\r
289\r
290 if (IsPassword) {\r
291 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);\r
292 }\r
293\r
294 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {\r
295 BufferedString[Count] = StringPtr[Index];\r
296\r
297 if (IsPassword) {\r
298 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');\r
299 }\r
300 }\r
301\r
302 if (!IsPassword) {\r
303 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
304 }\r
305 break;\r
306 }\r
307\r
308 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
309 gST->ConOut->SetCursorPosition (gST->ConOut, Start + CurrentCursor + 1, Top + 3);\r
310 } while (TRUE);\r
311\r
312}\r
313\r
314/**\r
315 Adjust the value to the correct one. Rules follow the sample:\r
316 like: Year change: 2012.02.29 -> 2013.02.29 -> 2013.02.01\r
317 Month change: 2013.03.29 -> 2013.02.29 -> 2013.02.28\r
318\r
319 @param QuestionValue Pointer to current question.\r
320 @param Sequence The sequence of the field in the question.\r
321**/\r
322VOID\r
323AdjustQuestionValue (\r
324 IN EFI_HII_VALUE *QuestionValue,\r
325 IN UINT8 Sequence\r
326 )\r
327{\r
328 UINT8 Month;\r
329 UINT16 Year;\r
330 UINT8 Maximum;\r
331 UINT8 Minimum;\r
332\r
333 Month = QuestionValue->Value.date.Month;\r
334 Year = QuestionValue->Value.date.Year;\r
335 Minimum = 1;\r
336\r
337 switch (Month) {\r
338 case 2:\r
339 if ((Year % 4) == 0 && ((Year % 100) != 0 || (Year % 400) == 0)) {\r
340 Maximum = 29;\r
341 } else {\r
342 Maximum = 28;\r
343 }\r
344 break;\r
345 case 4:\r
346 case 6:\r
347 case 9:\r
348 case 11:\r
349 Maximum = 30;\r
350 break;\r
351 default:\r
352 Maximum = 31;\r
353 break;\r
354 }\r
355\r
356 //\r
357 // Change the month area.\r
358 //\r
359 if (Sequence == 0) {\r
360 if (QuestionValue->Value.date.Day > Maximum) {\r
361 QuestionValue->Value.date.Day = Maximum;\r
362 }\r
363 }\r
d1102dba 364\r
7c6c064c
ED
365 //\r
366 // Change the Year area.\r
367 //\r
368 if (Sequence == 2) {\r
369 if (QuestionValue->Value.date.Day > Maximum) {\r
370 QuestionValue->Value.date.Day = Minimum;\r
371 }\r
372 }\r
373}\r
374\r
375/**\r
376 Get field info from numeric opcode.\r
377\r
378 @param OpCode Pointer to the current input opcode.\r
afc9bf79
ED
379 @param IntInput Whether question shows with EFI_IFR_DISPLAY_INT_DEC type.\r
380 @param QuestionValue Input question value, with EFI_HII_VALUE type.\r
381 @param Value Return question value, always return UINT64 type.\r
7c6c064c
ED
382 @param Minimum The minimum size info for this opcode.\r
383 @param Maximum The maximum size info for this opcode.\r
384 @param Step The step size info for this opcode.\r
385 @param StorageWidth The storage width info for this opcode.\r
386\r
387**/\r
388VOID\r
389GetValueFromNum (\r
390 IN EFI_IFR_OP_HEADER *OpCode,\r
afc9bf79
ED
391 IN BOOLEAN IntInput,\r
392 IN EFI_HII_VALUE *QuestionValue,\r
393 OUT UINT64 *Value,\r
7c6c064c
ED
394 OUT UINT64 *Minimum,\r
395 OUT UINT64 *Maximum,\r
396 OUT UINT64 *Step,\r
397 OUT UINT16 *StorageWidth\r
398)\r
399{\r
400 EFI_IFR_NUMERIC *NumericOp;\r
401\r
402 NumericOp = (EFI_IFR_NUMERIC *) OpCode;\r
d1102dba 403\r
7c6c064c
ED
404 switch (NumericOp->Flags & EFI_IFR_NUMERIC_SIZE) {\r
405 case EFI_IFR_NUMERIC_SIZE_1:\r
afc9bf79
ED
406 if (IntInput) {\r
407 *Minimum = (INT64) (INT8) NumericOp->data.u8.MinValue;\r
408 *Maximum = (INT64) (INT8) NumericOp->data.u8.MaxValue;\r
409 *Value = (INT64) (INT8) QuestionValue->Value.u8;\r
410 } else {\r
411 *Minimum = NumericOp->data.u8.MinValue;\r
412 *Maximum = NumericOp->data.u8.MaxValue;\r
413 *Value = QuestionValue->Value.u8;\r
414 }\r
7c6c064c
ED
415 *Step = NumericOp->data.u8.Step;\r
416 *StorageWidth = (UINT16) sizeof (UINT8);\r
417 break;\r
d1102dba 418\r
7c6c064c 419 case EFI_IFR_NUMERIC_SIZE_2:\r
afc9bf79
ED
420 if (IntInput) {\r
421 *Minimum = (INT64) (INT16) NumericOp->data.u16.MinValue;\r
422 *Maximum = (INT64) (INT16) NumericOp->data.u16.MaxValue;\r
423 *Value = (INT64) (INT16) QuestionValue->Value.u16;\r
424 } else {\r
425 *Minimum = NumericOp->data.u16.MinValue;\r
426 *Maximum = NumericOp->data.u16.MaxValue;\r
427 *Value = QuestionValue->Value.u16;\r
428 }\r
7c6c064c
ED
429 *Step = NumericOp->data.u16.Step;\r
430 *StorageWidth = (UINT16) sizeof (UINT16);\r
431 break;\r
d1102dba 432\r
7c6c064c 433 case EFI_IFR_NUMERIC_SIZE_4:\r
afc9bf79
ED
434 if (IntInput) {\r
435 *Minimum = (INT64) (INT32) NumericOp->data.u32.MinValue;\r
436 *Maximum = (INT64) (INT32) NumericOp->data.u32.MaxValue;\r
437 *Value = (INT64) (INT32) QuestionValue->Value.u32;\r
438 } else {\r
439 *Minimum = NumericOp->data.u32.MinValue;\r
440 *Maximum = NumericOp->data.u32.MaxValue;\r
441 *Value = QuestionValue->Value.u32;\r
442 }\r
7c6c064c
ED
443 *Step = NumericOp->data.u32.Step;\r
444 *StorageWidth = (UINT16) sizeof (UINT32);\r
445 break;\r
d1102dba 446\r
7c6c064c 447 case EFI_IFR_NUMERIC_SIZE_8:\r
afc9bf79
ED
448 if (IntInput) {\r
449 *Minimum = (INT64) NumericOp->data.u64.MinValue;\r
450 *Maximum = (INT64) NumericOp->data.u64.MaxValue;\r
451 *Value = (INT64) QuestionValue->Value.u64;\r
452 } else {\r
453 *Minimum = NumericOp->data.u64.MinValue;\r
454 *Maximum = NumericOp->data.u64.MaxValue;\r
455 *Value = QuestionValue->Value.u64;\r
456 }\r
7c6c064c
ED
457 *Step = NumericOp->data.u64.Step;\r
458 *StorageWidth = (UINT16) sizeof (UINT64);\r
459 break;\r
d1102dba 460\r
7c6c064c
ED
461 default:\r
462 break;\r
463 }\r
464\r
465 if (*Maximum == 0) {\r
466 *Maximum = (UINT64) -1;\r
467 }\r
468}\r
469\r
470/**\r
471 This routine reads a numeric value from the user input.\r
472\r
473 @param MenuOption Pointer to the current input menu.\r
474\r
475 @retval EFI_SUCCESS If numerical input is read successfully\r
476 @retval EFI_DEVICE_ERROR If operation fails\r
477\r
478**/\r
479EFI_STATUS\r
480GetNumericInput (\r
481 IN UI_MENU_OPTION *MenuOption\r
482 )\r
483{\r
7c6c064c
ED
484 UINTN Column;\r
485 UINTN Row;\r
486 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];\r
487 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];\r
488 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];\r
489 UINTN Count;\r
490 UINTN Loop;\r
491 BOOLEAN ManualInput;\r
492 BOOLEAN HexInput;\r
afc9bf79
ED
493 BOOLEAN IntInput;\r
494 BOOLEAN Negative;\r
495 BOOLEAN ValidateFail;\r
7c6c064c
ED
496 BOOLEAN DateOrTime;\r
497 UINTN InputWidth;\r
498 UINT64 EditValue;\r
499 UINT64 Step;\r
500 UINT64 Minimum;\r
501 UINT64 Maximum;\r
502 UINTN EraseLen;\r
503 UINT8 Digital;\r
504 EFI_INPUT_KEY Key;\r
505 EFI_HII_VALUE *QuestionValue;\r
506 FORM_DISPLAY_ENGINE_STATEMENT *Question;\r
507 EFI_IFR_NUMERIC *NumericOp;\r
508 UINT16 StorageWidth;\r
509\r
510 Column = MenuOption->OptCol;\r
511 Row = MenuOption->Row;\r
512 PreviousNumber[0] = 0;\r
513 Count = 0;\r
514 InputWidth = 0;\r
515 Digital = 0;\r
516 StorageWidth = 0;\r
517 Minimum = 0;\r
518 Maximum = 0;\r
519 NumericOp = NULL;\r
afc9bf79
ED
520 IntInput = FALSE;\r
521 HexInput = FALSE;\r
522 Negative = FALSE;\r
523 ValidateFail = FALSE;\r
7c6c064c
ED
524\r
525 Question = MenuOption->ThisTag;\r
526 QuestionValue = &Question->CurrentValue;\r
5c793e77 527 ZeroMem (InputText, MAX_NUMERIC_INPUT_WIDTH * sizeof (CHAR16));\r
7c6c064c
ED
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
d1102dba
LG
560 if ((QuestionValue->Value.date.Year % 4) == 0 &&\r
561 ((QuestionValue->Value.date.Year % 100) != 0 ||\r
7c6c064c
ED
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
d1102dba 577 }\r
7c6c064c
ED
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
5c793e77
DB
694 InputText[InputWidth + 1] = DATE_SEPARATOR;\r
695 InputText[InputWidth + 2] = L'\0';\r
696 } else if (MenuOption->Sequence == 1){\r
7c6c064c 697 SetUnicodeMem (InputText, InputWidth, L' ');\r
5c793e77
DB
698 InputText[InputWidth] = DATE_SEPARATOR;\r
699 InputText[InputWidth + 1] = L'\0';\r
7c6c064c 700 } else {\r
5c793e77
DB
701 SetUnicodeMem (InputText, InputWidth, L' ');\r
702 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;\r
703 InputText[InputWidth + 1] = L'\0';\r
7c6c064c 704 }\r
7c6c064c
ED
705\r
706 PrintStringAt (Column, Row, InputText);\r
707 if (MenuOption->Sequence == 0) {\r
708 Column++;\r
709 }\r
710 }\r
711\r
712 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
713 InputWidth = 2;\r
714\r
715 if (MenuOption->Sequence == 0) {\r
716 InputText[0] = LEFT_NUMERIC_DELIMITER;\r
717 SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
5c793e77
DB
718 InputText[InputWidth + 1] = TIME_SEPARATOR;\r
719 InputText[InputWidth + 2] = L'\0';\r
720 } else if (MenuOption->Sequence == 1){\r
7c6c064c 721 SetUnicodeMem (InputText, InputWidth, L' ');\r
5c793e77
DB
722 InputText[InputWidth] = TIME_SEPARATOR;\r
723 InputText[InputWidth + 1] = L'\0';\r
7c6c064c 724 } else {\r
5c793e77
DB
725 SetUnicodeMem (InputText, InputWidth, L' ');\r
726 InputText[InputWidth] = RIGHT_NUMERIC_DELIMITER;\r
727 InputText[InputWidth + 1] = L'\0';\r
7c6c064c 728 }\r
7c6c064c
ED
729\r
730 PrintStringAt (Column, Row, InputText);\r
731 if (MenuOption->Sequence == 0) {\r
732 Column++;\r
733 }\r
734 }\r
735 }\r
736\r
737 //\r
738 // First time we enter this handler, we need to check to see if\r
739 // we were passed an increment or decrement directive\r
740 //\r
741 do {\r
742 Key.UnicodeChar = CHAR_NULL;\r
743 if (gDirection != 0) {\r
744 Key.ScanCode = gDirection;\r
745 gDirection = 0;\r
746 goto TheKey2;\r
747 }\r
748\r
831537d6 749 WaitForKeyStroke (&Key);\r
7c6c064c
ED
750\r
751TheKey2:\r
752 switch (Key.UnicodeChar) {\r
753\r
754 case '+':\r
755 case '-':\r
afc9bf79
ED
756 if (ManualInput && IntInput) {\r
757 //\r
758 // In Manual input mode, check whether input the negative flag.\r
759 //\r
760 if (Key.UnicodeChar == '-') {\r
761 if (Negative) {\r
762 break;\r
763 }\r
764 Negative = TRUE;\r
765 PrintCharAt (Column++, Row, Key.UnicodeChar);\r
766 }\r
7c6c064c 767 } else {\r
afc9bf79
ED
768 if (Key.UnicodeChar == '+') {\r
769 Key.ScanCode = SCAN_RIGHT;\r
770 } else {\r
771 Key.ScanCode = SCAN_LEFT;\r
772 }\r
773 Key.UnicodeChar = CHAR_NULL;\r
774 goto TheKey2;\r
7c6c064c 775 }\r
afc9bf79 776 break;\r
7c6c064c
ED
777\r
778 case CHAR_NULL:\r
779 switch (Key.ScanCode) {\r
780 case SCAN_LEFT:\r
781 case SCAN_RIGHT:\r
782 if (DateOrTime && !ManualInput) {\r
783 //\r
784 // By setting this value, we will return back to the caller.\r
785 // We need to do this since an auto-refresh will destroy the adjustment\r
786 // based on what the real-time-clock is showing. So we always commit\r
787 // upon changing the value.\r
788 //\r
789 gDirection = SCAN_DOWN;\r
790 }\r
791\r
792 if ((Step != 0) && !ManualInput) {\r
793 if (Key.ScanCode == SCAN_LEFT) {\r
afc9bf79
ED
794 if (IntInput) {\r
795 if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {\r
796 EditValue = EditValue - Step;\r
797 } else if ((INT64) EditValue > (INT64) Minimum){\r
798 EditValue = Minimum;\r
799 } else {\r
800 EditValue = Maximum;\r
801 }\r
7c6c064c 802 } else {\r
afc9bf79
ED
803 if (EditValue >= Minimum + Step) {\r
804 EditValue = EditValue - Step;\r
805 } else if (EditValue > Minimum){\r
806 EditValue = Minimum;\r
807 } else {\r
808 EditValue = Maximum;\r
809 }\r
7c6c064c
ED
810 }\r
811 } else if (Key.ScanCode == SCAN_RIGHT) {\r
afc9bf79
ED
812 if (IntInput) {\r
813 if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {\r
814 EditValue = EditValue + Step;\r
815 } else if ((INT64) EditValue < (INT64) Maximum) {\r
816 EditValue = Maximum;\r
817 } else {\r
818 EditValue = Minimum;\r
819 }\r
7c6c064c 820 } else {\r
afc9bf79
ED
821 if (EditValue + Step <= Maximum) {\r
822 EditValue = EditValue + Step;\r
823 } else if (EditValue < Maximum) {\r
824 EditValue = Maximum;\r
825 } else {\r
826 EditValue = Minimum;\r
827 }\r
7c6c064c
ED
828 }\r
829 }\r
830\r
831 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
832 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
833 if (MenuOption->Sequence == 2) {\r
834 //\r
835 // Year\r
836 //\r
837 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);\r
838 } else {\r
839 //\r
840 // Month/Day\r
841 //\r
842 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
843 }\r
844\r
845 if (MenuOption->Sequence == 0) {\r
846 ASSERT (EraseLen >= 2);\r
847 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;\r
848 } else if (MenuOption->Sequence == 1) {\r
849 ASSERT (EraseLen >= 1);\r
850 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;\r
851 }\r
852 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
853 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
854\r
855 if (MenuOption->Sequence == 0) {\r
856 ASSERT (EraseLen >= 2);\r
857 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;\r
858 } else if (MenuOption->Sequence == 1) {\r
859 ASSERT (EraseLen >= 1);\r
860 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;\r
861 }\r
862 } else {\r
863 QuestionValue->Value.u64 = EditValue;\r
864 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
865 }\r
866\r
867 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());\r
868 for (Loop = 0; Loop < EraseLen; Loop++) {\r
869 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");\r
870 }\r
871 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());\r
872\r
873 if (MenuOption->Sequence == 0) {\r
874 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);\r
875 Column = MenuOption->OptCol + 1;\r
876 }\r
877\r
878 PrintStringAt (Column, Row, FormattedNumber);\r
879\r
880 if (!DateOrTime || MenuOption->Sequence == 2) {\r
881 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);\r
882 }\r
883 }\r
884\r
885 goto EnterCarriageReturn;\r
7c6c064c
ED
886\r
887 case SCAN_UP:\r
888 case SCAN_DOWN:\r
889 goto EnterCarriageReturn;\r
890\r
891 case SCAN_ESC:\r
892 return EFI_DEVICE_ERROR;\r
893\r
894 default:\r
895 break;\r
896 }\r
897\r
898 break;\r
899\r
900EnterCarriageReturn:\r
901\r
902 case CHAR_CARRIAGE_RETURN:\r
903 //\r
904 // Validate input value with Minimum value.\r
905 //\r
afc9bf79
ED
906 ValidateFail = FALSE;\r
907 if (IntInput) {\r
908 //\r
909 // After user input Enter, need to check whether the input value.\r
d1102dba 910 // If input a negative value, should compare with maximum value.\r
afc9bf79
ED
911 // else compare with the minimum value.\r
912 //\r
913 if (Negative) {\r
914 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;\r
915 } else {\r
916 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;\r
917 }\r
918\r
919 if (ValidateFail) {\r
920 UpdateStatusBar (INPUT_ERROR, TRUE);\r
921 break;\r
922 }\r
923 } else if (EditValue < Minimum) {\r
7c6c064c
ED
924 UpdateStatusBar (INPUT_ERROR, TRUE);\r
925 break;\r
7c6c064c 926 }\r
afc9bf79
ED
927\r
928 UpdateStatusBar (INPUT_ERROR, FALSE);\r
7c6c064c
ED
929 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));\r
930 QuestionValue = &gUserInput->InputValue;\r
931 //\r
932 // Store Edit value back to Question\r
933 //\r
934 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
935 switch (MenuOption->Sequence) {\r
936 case 0:\r
937 QuestionValue->Value.date.Month = (UINT8) EditValue;\r
938 break;\r
939\r
940 case 1:\r
941 QuestionValue->Value.date.Day = (UINT8) EditValue;\r
942 break;\r
943\r
944 case 2:\r
945 QuestionValue->Value.date.Year = (UINT16) EditValue;\r
946 break;\r
947\r
948 default:\r
949 break;\r
950 }\r
951 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
952 switch (MenuOption->Sequence) {\r
953 case 0:\r
954 QuestionValue->Value.time.Hour = (UINT8) EditValue;\r
955 break;\r
956\r
957 case 1:\r
958 QuestionValue->Value.time.Minute = (UINT8) EditValue;\r
959 break;\r
960\r
961 case 2:\r
962 QuestionValue->Value.time.Second = (UINT8) EditValue;\r
963 break;\r
964\r
965 default:\r
966 break;\r
967 }\r
968 } else {\r
969 //\r
970 // Numeric\r
971 //\r
972 QuestionValue->Value.u64 = EditValue;\r
973 }\r
974\r
975 //\r
976 // Adjust the value to the correct one.\r
977 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01\r
978 // 2013.03.29 -> 2013.02.29 -> 2013.02.28\r
979 //\r
d1102dba 980 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP &&\r
7c6c064c
ED
981 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {\r
982 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);\r
983 }\r
984\r
bfae1330 985 return EFI_SUCCESS;\r
7c6c064c
ED
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
bce1d8a8 1076 ASSERT (Count < ARRAY_SIZE (PreviousNumber));\r
afc9bf79
ED
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
bce1d8a8 1083 ASSERT (Count < ARRAY_SIZE (PreviousNumber));\r
afc9bf79
ED
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 1091 Count++;\r
bce1d8a8 1092 ASSERT (Count < (ARRAY_SIZE (PreviousNumber)));\r
7c6c064c
ED
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
d1102dba 1139\r
7c6c064c 1140 *PopUpMenuLines = Index;\r
d1102dba 1141\r
7c6c064c
ED
1142 //\r
1143 // Prepare HiiValue array\r
d1102dba 1144 //\r
7c6c064c
ED
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
d1102dba 1152\r
7c6c064c
ED
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
d1102dba 1158\r
7c6c064c 1159 RemoveEntryList (&OneOfOption->Link);\r
d1102dba 1160\r
7c6c064c
ED
1161 //\r
1162 // Insert to head.\r
1163 //\r
1164 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
1165 }\r
d1102dba 1166\r
7c6c064c
ED
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
d1102dba 1194\r
7c6c064c 1195 case EFI_IFR_TYPE_NUM_SIZE_16:\r
5a9f73bf 1196 return (BOOLEAN) (Value1->u16 == Value2->u16);\r
d1102dba 1197\r
7c6c064c 1198 case EFI_IFR_TYPE_NUM_SIZE_32:\r
5a9f73bf 1199 return (BOOLEAN) (Value1->u32 == Value2->u32);\r
d1102dba 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
7c6c064c
ED
1305 ZeroMem (&HiiValue, sizeof (EFI_HII_VALUE));\r
1306\r
1307 Question = MenuOption->ThisTag;\r
1308 if (Question->OpCode->OpCode == EFI_IFR_ORDERED_LIST_OP) {\r
1309 Link = GetFirstNode (&Question->OptionListHead);\r
1310 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1311 ValueArray = Question->CurrentValue.Buffer;\r
1312 ValueType = OneOfOption->OptionOpCode->Type;\r
1313 OrderedList = TRUE;\r
1314 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;\r
1315 } else {\r
1316 OrderedList = FALSE;\r
1317 OrderList = NULL;\r
1318 }\r
1319\r
1320 //\r
1321 // Calculate Option count\r
1322 //\r
1323 PopUpMenuLines = 0;\r
1324 if (OrderedList) {\r
1325 AdjustOptionOrder(Question, &PopUpMenuLines);\r
1326 } else {\r
1327 Link = GetFirstNode (&Question->OptionListHead);\r
1328 while (!IsNull (&Question->OptionListHead, Link)) {\r
1329 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1330 PopUpMenuLines++;\r
1331 Link = GetNextNode (&Question->OptionListHead, Link);\r
1332 }\r
1333 }\r
1334\r
1335 //\r
1336 // Get the number of one of options present and its size\r
1337 //\r
1338 PopUpWidth = 0;\r
1339 HighlightOptionIndex = 0;\r
1340 Link = GetFirstNode (&Question->OptionListHead);\r
1341 for (Index = 0; Index < PopUpMenuLines; Index++) {\r
1342 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1343\r
1344 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);\r
1345 if (StrLen (StringPtr) > PopUpWidth) {\r
1346 PopUpWidth = StrLen (StringPtr);\r
1347 }\r
1348 FreePool (StringPtr);\r
1349 HiiValue.Type = OneOfOption->OptionOpCode->Type;\r
1350 SetValuesByType (&HiiValue.Value, &OneOfOption->OptionOpCode->Value, HiiValue.Type);\r
1351 if (!OrderedList && (CompareHiiValue (&Question->CurrentValue, &HiiValue, &Result, NULL) == EFI_SUCCESS) && (Result == 0)) {\r
1352 //\r
1353 // Find current selected Option for OneOf\r
1354 //\r
1355 HighlightOptionIndex = Index;\r
1356 }\r
1357\r
1358 Link = GetNextNode (&Question->OptionListHead, Link);\r
1359 }\r
1360\r
1361 //\r
1362 // Perform popup menu initialization.\r
1363 //\r
1364 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
1365\r
1366 SavedAttribute = gST->ConOut->Mode->Attribute;\r
1367 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());\r
1368\r
1369 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
1370 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
1371 }\r
1372\r
1373 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gStatementDimensions.LeftColumn;\r
1374 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
1375 Top = gStatementDimensions.TopRow;\r
1376 Bottom = gStatementDimensions.BottomRow - 1;\r
1377\r
1378 MenuLinesInView = Bottom - Top - 1;\r
1379 if (MenuLinesInView >= PopUpMenuLines) {\r
1380 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
1381 Bottom = Top + PopUpMenuLines + 1;\r
1382 } else {\r
1383 ShowDownArrow = TRUE;\r
1384 }\r
1385\r
1386 if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
1387 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
1388 } else {\r
1389 TopOptionIndex = 0;\r
1390 }\r
1391\r
1392 do {\r
1393 //\r
1394 // Clear that portion of the screen\r
1395 //\r
1396 ClearLines (Start, End, Top, Bottom, GetPopupColor ());\r
1397\r
1398 //\r
1399 // Draw "One of" pop-up menu\r
1400 //\r
1401 Character = BOXDRAW_DOWN_RIGHT;\r
1402 PrintCharAt (Start, Top, Character);\r
1403 for (Index = Start; Index + 2 < End; Index++) {\r
1404 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
1405 Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
1406 } else {\r
1407 Character = BOXDRAW_HORIZONTAL;\r
1408 }\r
1409\r
1410 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
1411 }\r
1412\r
1413 Character = BOXDRAW_DOWN_LEFT;\r
1414 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
1415 Character = BOXDRAW_VERTICAL;\r
1416 for (Index = Top + 1; Index < Bottom; Index++) {\r
1417 PrintCharAt (Start, Index, Character);\r
1418 PrintCharAt (End - 1, Index, Character);\r
1419 }\r
1420\r
1421 //\r
1422 // Move to top Option\r
1423 //\r
1424 Link = GetFirstNode (&Question->OptionListHead);\r
1425 for (Index = 0; Index < TopOptionIndex; Index++) {\r
1426 Link = GetNextNode (&Question->OptionListHead, Link);\r
1427 }\r
1428\r
1429 //\r
1430 // Display the One of options\r
1431 //\r
1432 Index2 = Top + 1;\r
1433 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
1434 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1435 Link = GetNextNode (&Question->OptionListHead, Link);\r
1436\r
1437 StringPtr = GetToken (OneOfOption->OptionOpCode->Option, gFormData->HiiHandle);\r
1438 ASSERT (StringPtr != NULL);\r
1439 //\r
1440 // If the string occupies multiple lines, truncate it to fit in one line,\r
1441 // and append a "..." for indication.\r
1442 //\r
1443 if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
1444 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
1445 ASSERT ( TempStringPtr != NULL );\r
1446 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
1447 FreePool (StringPtr);\r
1448 StringPtr = TempStringPtr;\r
5ad66ec6 1449 StrCatS (StringPtr, PopUpWidth - 1, L"...");\r
7c6c064c
ED
1450 }\r
1451\r
1452 if (Index == HighlightOptionIndex) {\r
1453 //\r
1454 // Highlight the selected one\r
1455 //\r
1456 CurrentOption = OneOfOption;\r
1457\r
1458 gST->ConOut->SetAttribute (gST->ConOut, GetPickListColor ());\r
1459 PrintStringAt (Start + 2, Index2, StringPtr);\r
1460 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());\r
1461 } else {\r
1462 gST->ConOut->SetAttribute (gST->ConOut, GetPopupColor ());\r
1463 PrintStringAt (Start + 2, Index2, StringPtr);\r
1464 }\r
1465\r
1466 Index2++;\r
1467 FreePool (StringPtr);\r
1468 }\r
1469\r
1470 Character = BOXDRAW_UP_RIGHT;\r
1471 PrintCharAt (Start, Bottom, Character);\r
1472 for (Index = Start; Index + 2 < End; Index++) {\r
1473 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
1474 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
1475 } else {\r
1476 Character = BOXDRAW_HORIZONTAL;\r
1477 }\r
1478\r
1479 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
1480 }\r
1481\r
1482 Character = BOXDRAW_UP_LEFT;\r
1483 PrintCharAt ((UINTN)-1, (UINTN)-1, Character);\r
1484\r
1485 //\r
1486 // Get User selection\r
1487 //\r
1488 Key.UnicodeChar = CHAR_NULL;\r
1489 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
1490 Key.ScanCode = gDirection;\r
1491 gDirection = 0;\r
1492 goto TheKey;\r
1493 }\r
1494\r
831537d6 1495 WaitForKeyStroke (&Key);\r
7c6c064c
ED
1496\r
1497TheKey:\r
1498 switch (Key.UnicodeChar) {\r
1499 case '+':\r
1500 if (OrderedList) {\r
1501 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1502 //\r
1503 // Highlight reaches the top of the popup window, scroll one menu item.\r
1504 //\r
1505 TopOptionIndex--;\r
1506 ShowDownArrow = TRUE;\r
1507 }\r
1508\r
1509 if (TopOptionIndex == 0) {\r
1510 ShowUpArrow = FALSE;\r
1511 }\r
1512\r
1513 if (HighlightOptionIndex > 0) {\r
1514 HighlightOptionIndex--;\r
1515\r
1516 ASSERT (CurrentOption != NULL);\r
1517 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
1518 }\r
1519 }\r
1520 break;\r
1521\r
1522 case '-':\r
1523 //\r
1524 // If an ordered list op-code, we will allow for a popup of +/- keys\r
1525 // to create an ordered list of items\r
1526 //\r
1527 if (OrderedList) {\r
1528 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1529 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1530 //\r
1531 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1532 //\r
1533 TopOptionIndex++;\r
1534 ShowUpArrow = TRUE;\r
1535 }\r
1536\r
1537 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1538 ShowDownArrow = FALSE;\r
1539 }\r
1540\r
1541 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1542 HighlightOptionIndex++;\r
1543\r
1544 ASSERT (CurrentOption != NULL);\r
1545 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
1546 }\r
1547 }\r
1548 break;\r
1549\r
1550 case CHAR_NULL:\r
1551 switch (Key.ScanCode) {\r
1552 case SCAN_UP:\r
1553 case SCAN_DOWN:\r
1554 if (Key.ScanCode == SCAN_UP) {\r
1555 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1556 //\r
1557 // Highlight reaches the top of the popup window, scroll one menu item.\r
1558 //\r
1559 TopOptionIndex--;\r
1560 ShowDownArrow = TRUE;\r
1561 }\r
1562\r
1563 if (TopOptionIndex == 0) {\r
1564 ShowUpArrow = FALSE;\r
1565 }\r
1566\r
1567 if (HighlightOptionIndex > 0) {\r
1568 HighlightOptionIndex--;\r
1569 }\r
1570 } else {\r
1571 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1572 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1573 //\r
1574 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1575 //\r
1576 TopOptionIndex++;\r
1577 ShowUpArrow = TRUE;\r
1578 }\r
1579\r
1580 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1581 ShowDownArrow = FALSE;\r
1582 }\r
1583\r
1584 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1585 HighlightOptionIndex++;\r
1586 }\r
1587 }\r
1588 break;\r
1589\r
1590 case SCAN_ESC:\r
1591 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1592\r
1593 //\r
1594 // Restore link list order for orderedlist\r
1595 //\r
1596 if (OrderedList) {\r
1597 HiiValue.Type = ValueType;\r
1598 HiiValue.Value.u64 = 0;\r
1599 for (Index = 0; Index < OrderList->MaxContainers; Index++) {\r
1600 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
1601 if (HiiValue.Value.u64 == 0) {\r
1602 break;\r
1603 }\r
1604\r
1605 OneOfOption = ValueToOption (Question, &HiiValue);\r
1606 if (OneOfOption == NULL) {\r
1607 return EFI_NOT_FOUND;\r
1608 }\r
1609\r
1610 RemoveEntryList (&OneOfOption->Link);\r
1611 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
1612 }\r
1613 }\r
1614\r
1615 return EFI_DEVICE_ERROR;\r
1616\r
1617 default:\r
1618 break;\r
1619 }\r
1620\r
1621 break;\r
1622\r
1623 case CHAR_CARRIAGE_RETURN:\r
1624 //\r
1625 // return the current selection\r
1626 //\r
1627 if (OrderedList) {\r
1628 ReturnValue = AllocateZeroPool (Question->CurrentValue.BufferLen);\r
1629 ASSERT (ReturnValue != NULL);\r
1630 Index = 0;\r
1631 Link = GetFirstNode (&Question->OptionListHead);\r
1632 while (!IsNull (&Question->OptionListHead, Link)) {\r
1633 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1634 Link = GetNextNode (&Question->OptionListHead, Link);\r
1635\r
1636 SetArrayData (ReturnValue, ValueType, Index, OneOfOption->OptionOpCode->Value.u64);\r
1637\r
1638 Index++;\r
1639 if (Index > OrderList->MaxContainers) {\r
1640 break;\r
1641 }\r
1642 }\r
1643 if (CompareMem (ReturnValue, ValueArray, Question->CurrentValue.BufferLen) == 0) {\r
1644 FreePool (ReturnValue);\r
1645 return EFI_DEVICE_ERROR;\r
1646 } else {\r
1647 gUserInput->InputValue.Buffer = ReturnValue;\r
1648 gUserInput->InputValue.BufferLen = Question->CurrentValue.BufferLen;\r
7c6c064c
ED
1649 }\r
1650 } else {\r
1651 ASSERT (CurrentOption != NULL);\r
1652 gUserInput->InputValue.Type = CurrentOption->OptionOpCode->Type;\r
1653 if (IsValuesEqual (&Question->CurrentValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type)) {\r
1654 return EFI_DEVICE_ERROR;\r
1655 } else {\r
1656 SetValuesByType (&gUserInput->InputValue.Value, &CurrentOption->OptionOpCode->Value, gUserInput->InputValue.Type);\r
7c6c064c
ED
1657 }\r
1658 }\r
1659\r
1660 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1661\r
bfae1330 1662 return EFI_SUCCESS;\r
d1102dba 1663\r
7c6c064c
ED
1664 default:\r
1665 break;\r
1666 }\r
1667 } while (TRUE);\r
1668\r
1669}\r
1670\r