]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/DisplayEngineDxe/InputHandler.c
MdeModulePkg:Fix the potential memory leak issue in Display Engine
[mirror_edk2.git] / MdeModulePkg / Universal / DisplayEngineDxe / InputHandler.c
CommitLineData
7c6c064c
ED
1/** @file\r
2Implementation for handling user input from the User Interfaces.\r
3\r
5ad66ec6 4Copyright (c) 2004 - 2015, Intel Corporation. All rights reserved.<BR>\r
7c6c064c
ED
5This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "FormDisplay.h"\r
16\r
17/**\r
18 Get maximum and minimum info from this opcode.\r
19\r
20 @param OpCode Pointer to the current input opcode.\r
21 @param Minimum The minimum size info for this opcode.\r
22 @param Maximum The maximum size info for this opcode.\r
23\r
24**/\r
25VOID\r
26GetFieldFromOp (\r
27 IN EFI_IFR_OP_HEADER *OpCode,\r
28 OUT UINTN *Minimum,\r
29 OUT UINTN *Maximum\r
30 )\r
31{\r
32 EFI_IFR_STRING *StringOp;\r
33 EFI_IFR_PASSWORD *PasswordOp;\r
34 if (OpCode->OpCode == EFI_IFR_STRING_OP) {\r
35 StringOp = (EFI_IFR_STRING *) OpCode;\r
36 *Minimum = StringOp->MinSize;\r
37 *Maximum = StringOp->MaxSize; \r
38 } else if (OpCode->OpCode == EFI_IFR_PASSWORD_OP) {\r
39 PasswordOp = (EFI_IFR_PASSWORD *) OpCode;\r
40 *Minimum = PasswordOp->MinSize;\r
41 *Maximum = PasswordOp->MaxSize; \r
42 } else {\r
43 *Minimum = 0;\r
44 *Maximum = 0; \r
45 }\r
46}\r
47\r
48/**\r
49 Get string or password input from user.\r
50\r
51 @param MenuOption Pointer to the current input menu.\r
52 @param Prompt The prompt string shown on popup window.\r
53 @param StringPtr Old user input and destination for use input string.\r
54\r
55 @retval EFI_SUCCESS If string input is read successfully\r
56 @retval EFI_DEVICE_ERROR If operation fails\r
57\r
58**/\r
59EFI_STATUS\r
60ReadString (\r
61 IN UI_MENU_OPTION *MenuOption,\r
62 IN CHAR16 *Prompt,\r
63 IN OUT CHAR16 *StringPtr\r
64 )\r
65{\r
66 EFI_STATUS Status;\r
67 EFI_INPUT_KEY Key;\r
68 CHAR16 NullCharacter;\r
69 UINTN ScreenSize;\r
70 CHAR16 Space[2];\r
71 CHAR16 KeyPad[2];\r
72 CHAR16 *TempString;\r
73 CHAR16 *BufferedString;\r
74 UINTN Index;\r
75 UINTN Index2;\r
76 UINTN Count;\r
77 UINTN Start;\r
78 UINTN Top;\r
79 UINTN DimensionsWidth;\r
80 UINTN DimensionsHeight;\r
81 UINTN CurrentCursor;\r
82 BOOLEAN CursorVisible;\r
83 UINTN Minimum;\r
84 UINTN Maximum;\r
85 FORM_DISPLAY_ENGINE_STATEMENT *Question;\r
86 BOOLEAN IsPassword;\r
5ad66ec6 87 UINTN MaxLen;\r
7c6c064c
ED
88\r
89 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;\r
90 DimensionsHeight = gStatementDimensions.BottomRow - gStatementDimensions.TopRow;\r
91\r
92 NullCharacter = CHAR_NULL;\r
93 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);\r
94 Space[0] = L' ';\r
95 Space[1] = CHAR_NULL;\r
96\r
97 Question = MenuOption->ThisTag;\r
98 GetFieldFromOp(Question->OpCode, &Minimum, &Maximum);\r
99\r
100 if (Question->OpCode->OpCode == EFI_IFR_PASSWORD_OP) {\r
101 IsPassword = TRUE;\r
102 } else {\r
103 IsPassword = FALSE;\r
104 }\r
105\r
5ad66ec6
DB
106 MaxLen = Maximum + 1;\r
107 TempString = AllocateZeroPool (MaxLen * sizeof (CHAR16));\r
7c6c064c
ED
108 ASSERT (TempString);\r
109\r
110 if (ScreenSize < (Maximum + 1)) {\r
111 ScreenSize = Maximum + 1;\r
112 }\r
113\r
114 if ((ScreenSize + 2) > DimensionsWidth) {\r
115 ScreenSize = DimensionsWidth - 2;\r
116 }\r
117\r
118 BufferedString = AllocateZeroPool (ScreenSize * 2);\r
119 ASSERT (BufferedString);\r
120\r
121 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gStatementDimensions.LeftColumn + 1;\r
122 Top = ((DimensionsHeight - 6) / 2) + gStatementDimensions.TopRow - 1;\r
123\r
124 //\r
125 // Display prompt for string\r
126 //\r
127 // CreateDialog (NULL, "", Prompt, Space, "", NULL);\r
128 CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);\r
129 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
130\r
131 CursorVisible = gST->ConOut->Mode->CursorVisible;\r
132 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
133\r
134 CurrentCursor = GetStringWidth (StringPtr) / 2 - 1;\r
135 if (CurrentCursor != 0) {\r
136 //\r
137 // Show the string which has beed saved before.\r
138 //\r
139 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');\r
140 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
141\r
142 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {\r
143 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;\r
144 } else {\r
145 Index = 0;\r
146 }\r
147\r
148 if (IsPassword) {\r
149 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);\r
150 }\r
151\r
152 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {\r
153 BufferedString[Count] = StringPtr[Index];\r
154\r
155 if (IsPassword) {\r
156 PrintCharAt ((UINTN)-1, (UINTN)-1, L'*');\r
157 }\r
158 }\r
159\r
160 if (!IsPassword) {\r
161 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
162 }\r
163 \r
164 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
165 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);\r
166 }\r
167 \r
168 do {\r
169 Status = WaitForKeyStroke (&Key);\r
170 ASSERT_EFI_ERROR (Status);\r
171\r
172 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
173 switch (Key.UnicodeChar) {\r
174 case CHAR_NULL:\r
175 switch (Key.ScanCode) {\r
176 case SCAN_LEFT:\r
177 if (CurrentCursor > 0) {\r
178 CurrentCursor--;\r
179 }\r
180 break;\r
181\r
182 case SCAN_RIGHT:\r
183 if (CurrentCursor < (GetStringWidth (StringPtr) / 2 - 1)) {\r
184 CurrentCursor++;\r
185 }\r
186 break;\r
187\r
188 case SCAN_ESC:\r
189 FreePool (TempString);\r
190 FreePool (BufferedString);\r
191 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
192 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
193 return EFI_DEVICE_ERROR;\r
194\r
1e9c7229
DB
195 case SCAN_DELETE:\r
196 for (Index = CurrentCursor; StringPtr[Index] != CHAR_NULL; Index++) {\r
197 StringPtr[Index] = StringPtr[Index + 1];\r
198 PrintCharAt (Start + Index + 1, Top + 3, IsPassword && StringPtr[Index] != CHAR_NULL? L'*' : StringPtr[Index]);\r
199 }\r
200 break;\r
201\r
7c6c064c
ED
202 default:\r
203 break;\r
204 }\r
205\r
206 break;\r
207\r
208 case CHAR_CARRIAGE_RETURN:\r
209 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {\r
210\r
211 FreePool (TempString);\r
212 FreePool (BufferedString);\r
213 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
214 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
215 return EFI_SUCCESS;\r
216 } else {\r
217 //\r
218 // Simply create a popup to tell the user that they had typed in too few characters.\r
219 // To save code space, we can then treat this as an error and return back to the menu.\r
220 //\r
221 do {\r
222 CreateDialog (&Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter, NULL);\r
223 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
224\r
225 FreePool (TempString);\r
226 FreePool (BufferedString);\r
227 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
228 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
229 return EFI_DEVICE_ERROR;\r
230 }\r
231\r
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
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
364 \r
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
403 \r
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
418 \r
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
432 \r
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
446 \r
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
460 \r
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
527\r
528 //\r
529 // Only two case, user can enter to this function: Enter and +/- case.\r
530 // In Enter case, gDirection = 0; in +/- case, gDirection = SCAN_LEFT/SCAN_WRIGHT\r
531 //\r
532 ManualInput = (BOOLEAN)(gDirection == 0 ? TRUE : FALSE);\r
533\r
534 if ((Question->OpCode->OpCode == EFI_IFR_DATE_OP) || (Question->OpCode->OpCode == EFI_IFR_TIME_OP)) {\r
535 DateOrTime = TRUE;\r
536 } else {\r
537 DateOrTime = FALSE;\r
538 }\r
539\r
540 //\r
541 // Prepare Value to be edit\r
542 //\r
543 EraseLen = 0;\r
544 EditValue = 0;\r
545 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
546 Step = 1;\r
547 Minimum = 1;\r
548\r
549 switch (MenuOption->Sequence) {\r
550 case 0:\r
551 Maximum = 12;\r
552 EraseLen = 4;\r
553 EditValue = QuestionValue->Value.date.Month;\r
554 break;\r
555\r
556 case 1:\r
557 switch (QuestionValue->Value.date.Month) {\r
558 case 2:\r
559 if ((QuestionValue->Value.date.Year % 4) == 0 && \r
560 ((QuestionValue->Value.date.Year % 100) != 0 || \r
561 (QuestionValue->Value.date.Year % 400) == 0)) {\r
562 Maximum = 29;\r
563 } else {\r
564 Maximum = 28;\r
565 }\r
566 break;\r
567 case 4:\r
568 case 6:\r
569 case 9:\r
570 case 11:\r
571 Maximum = 30;\r
572 break;\r
573 default:\r
574 Maximum = 31;\r
575 break;\r
576 } \r
577\r
578 EraseLen = 3;\r
579 EditValue = QuestionValue->Value.date.Day;\r
580 break;\r
581\r
582 case 2:\r
583 Maximum = 0xffff;\r
584 EraseLen = 5;\r
585 EditValue = QuestionValue->Value.date.Year;\r
586 break;\r
587\r
588 default:\r
589 break;\r
590 }\r
591 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
592 Step = 1;\r
593 Minimum = 0;\r
594\r
595 switch (MenuOption->Sequence) {\r
596 case 0:\r
597 Maximum = 23;\r
598 EraseLen = 4;\r
599 EditValue = QuestionValue->Value.time.Hour;\r
600 break;\r
601\r
602 case 1:\r
603 Maximum = 59;\r
604 EraseLen = 3;\r
605 EditValue = QuestionValue->Value.time.Minute;\r
606 break;\r
607\r
608 case 2:\r
609 Maximum = 59;\r
610 EraseLen = 3;\r
611 EditValue = QuestionValue->Value.time.Second;\r
612 break;\r
613\r
614 default:\r
615 break;\r
616 }\r
617 } else {\r
618 ASSERT (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP);\r
619 NumericOp = (EFI_IFR_NUMERIC *) Question->OpCode;\r
afc9bf79 620 GetValueFromNum(Question->OpCode, (NumericOp->Flags & EFI_IFR_DISPLAY) == 0, QuestionValue, &EditValue, &Minimum, &Maximum, &Step, &StorageWidth);\r
7c6c064c
ED
621 EraseLen = gOptionBlockWidth;\r
622 }\r
623\r
afc9bf79
ED
624 if ((Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) && (NumericOp != NULL)) {\r
625 if ((NumericOp->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX){\r
626 HexInput = TRUE;\r
627 } else if ((NumericOp->Flags & EFI_IFR_DISPLAY) == 0){\r
628 //\r
629 // Display with EFI_IFR_DISPLAY_INT_DEC type. Support negative number.\r
630 //\r
631 IntInput = TRUE;\r
632 }\r
7c6c064c
ED
633 }\r
634\r
635 //\r
636 // Enter from "Enter" input, clear the old word showing.\r
637 //\r
638 if (ManualInput) {\r
639 if (Question->OpCode->OpCode == EFI_IFR_NUMERIC_OP) {\r
640 if (HexInput) {\r
641 InputWidth = StorageWidth * 2;\r
642 } else {\r
643 switch (StorageWidth) {\r
644 case 1:\r
645 InputWidth = 3;\r
646 break;\r
647\r
648 case 2:\r
649 InputWidth = 5;\r
650 break;\r
651\r
652 case 4:\r
653 InputWidth = 10;\r
654 break;\r
655\r
656 case 8:\r
657 InputWidth = 20;\r
658 break;\r
659\r
660 default:\r
661 InputWidth = 0;\r
662 break;\r
663 }\r
afc9bf79
ED
664\r
665 if (IntInput) {\r
666 //\r
667 // Support an extra '-' for negative number.\r
668 //\r
669 InputWidth += 1;\r
670 }\r
7c6c064c
ED
671 }\r
672\r
673 InputText[0] = LEFT_NUMERIC_DELIMITER;\r
674 SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
675 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH);\r
676 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
677 InputText[InputWidth + 2] = L'\0';\r
678\r
679 PrintStringAt (Column, Row, InputText);\r
680 Column++;\r
681 }\r
682\r
683 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
684 if (MenuOption->Sequence == 2) {\r
685 InputWidth = 4;\r
686 } else {\r
687 InputWidth = 2;\r
688 }\r
689\r
690 if (MenuOption->Sequence == 0) {\r
691 InputText[0] = LEFT_NUMERIC_DELIMITER;\r
692 SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
693 } else {\r
694 SetUnicodeMem (InputText, InputWidth, L' ');\r
695 }\r
696\r
697 if (MenuOption->Sequence == 2) {\r
698 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
699 } else {\r
700 InputText[InputWidth + 1] = DATE_SEPARATOR;\r
701 }\r
702 InputText[InputWidth + 2] = L'\0';\r
703\r
704 PrintStringAt (Column, Row, InputText);\r
705 if (MenuOption->Sequence == 0) {\r
706 Column++;\r
707 }\r
708 }\r
709\r
710 if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
711 InputWidth = 2;\r
712\r
713 if (MenuOption->Sequence == 0) {\r
714 InputText[0] = LEFT_NUMERIC_DELIMITER;\r
715 SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
716 } else {\r
717 SetUnicodeMem (InputText, InputWidth, L' ');\r
718 }\r
719\r
720 if (MenuOption->Sequence == 2) {\r
721 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
722 } else {\r
723 InputText[InputWidth + 1] = TIME_SEPARATOR;\r
724 }\r
725 InputText[InputWidth + 2] = L'\0';\r
726\r
727 PrintStringAt (Column, Row, InputText);\r
728 if (MenuOption->Sequence == 0) {\r
729 Column++;\r
730 }\r
731 }\r
732 }\r
733\r
734 //\r
735 // First time we enter this handler, we need to check to see if\r
736 // we were passed an increment or decrement directive\r
737 //\r
738 do {\r
739 Key.UnicodeChar = CHAR_NULL;\r
740 if (gDirection != 0) {\r
741 Key.ScanCode = gDirection;\r
742 gDirection = 0;\r
743 goto TheKey2;\r
744 }\r
745\r
831537d6 746 WaitForKeyStroke (&Key);\r
7c6c064c
ED
747\r
748TheKey2:\r
749 switch (Key.UnicodeChar) {\r
750\r
751 case '+':\r
752 case '-':\r
afc9bf79
ED
753 if (ManualInput && IntInput) {\r
754 //\r
755 // In Manual input mode, check whether input the negative flag.\r
756 //\r
757 if (Key.UnicodeChar == '-') {\r
758 if (Negative) {\r
759 break;\r
760 }\r
761 Negative = TRUE;\r
762 PrintCharAt (Column++, Row, Key.UnicodeChar);\r
763 }\r
7c6c064c 764 } else {\r
afc9bf79
ED
765 if (Key.UnicodeChar == '+') {\r
766 Key.ScanCode = SCAN_RIGHT;\r
767 } else {\r
768 Key.ScanCode = SCAN_LEFT;\r
769 }\r
770 Key.UnicodeChar = CHAR_NULL;\r
771 goto TheKey2;\r
7c6c064c 772 }\r
afc9bf79 773 break;\r
7c6c064c
ED
774\r
775 case CHAR_NULL:\r
776 switch (Key.ScanCode) {\r
777 case SCAN_LEFT:\r
778 case SCAN_RIGHT:\r
779 if (DateOrTime && !ManualInput) {\r
780 //\r
781 // By setting this value, we will return back to the caller.\r
782 // We need to do this since an auto-refresh will destroy the adjustment\r
783 // based on what the real-time-clock is showing. So we always commit\r
784 // upon changing the value.\r
785 //\r
786 gDirection = SCAN_DOWN;\r
787 }\r
788\r
789 if ((Step != 0) && !ManualInput) {\r
790 if (Key.ScanCode == SCAN_LEFT) {\r
afc9bf79
ED
791 if (IntInput) {\r
792 if ((INT64) EditValue >= (INT64) Minimum + (INT64) Step) {\r
793 EditValue = EditValue - Step;\r
794 } else if ((INT64) EditValue > (INT64) Minimum){\r
795 EditValue = Minimum;\r
796 } else {\r
797 EditValue = Maximum;\r
798 }\r
7c6c064c 799 } else {\r
afc9bf79
ED
800 if (EditValue >= Minimum + Step) {\r
801 EditValue = EditValue - Step;\r
802 } else if (EditValue > Minimum){\r
803 EditValue = Minimum;\r
804 } else {\r
805 EditValue = Maximum;\r
806 }\r
7c6c064c
ED
807 }\r
808 } else if (Key.ScanCode == SCAN_RIGHT) {\r
afc9bf79
ED
809 if (IntInput) {\r
810 if ((INT64) EditValue + (INT64) Step <= (INT64) Maximum) {\r
811 EditValue = EditValue + Step;\r
812 } else if ((INT64) EditValue < (INT64) Maximum) {\r
813 EditValue = Maximum;\r
814 } else {\r
815 EditValue = Minimum;\r
816 }\r
7c6c064c 817 } else {\r
afc9bf79
ED
818 if (EditValue + Step <= Maximum) {\r
819 EditValue = EditValue + Step;\r
820 } else if (EditValue < Maximum) {\r
821 EditValue = Maximum;\r
822 } else {\r
823 EditValue = Minimum;\r
824 }\r
7c6c064c
ED
825 }\r
826 }\r
827\r
828 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
829 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
830 if (MenuOption->Sequence == 2) {\r
831 //\r
832 // Year\r
833 //\r
834 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", (UINT16) EditValue);\r
835 } else {\r
836 //\r
837 // Month/Day\r
838 //\r
839 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
840 }\r
841\r
842 if (MenuOption->Sequence == 0) {\r
843 ASSERT (EraseLen >= 2);\r
844 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;\r
845 } else if (MenuOption->Sequence == 1) {\r
846 ASSERT (EraseLen >= 1);\r
847 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;\r
848 }\r
849 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
850 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", (UINT8) EditValue);\r
851\r
852 if (MenuOption->Sequence == 0) {\r
853 ASSERT (EraseLen >= 2);\r
854 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;\r
855 } else if (MenuOption->Sequence == 1) {\r
856 ASSERT (EraseLen >= 1);\r
857 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;\r
858 }\r
859 } else {\r
860 QuestionValue->Value.u64 = EditValue;\r
861 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
862 }\r
863\r
864 gST->ConOut->SetAttribute (gST->ConOut, GetFieldTextColor ());\r
865 for (Loop = 0; Loop < EraseLen; Loop++) {\r
866 PrintStringAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");\r
867 }\r
868 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());\r
869\r
870 if (MenuOption->Sequence == 0) {\r
871 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);\r
872 Column = MenuOption->OptCol + 1;\r
873 }\r
874\r
875 PrintStringAt (Column, Row, FormattedNumber);\r
876\r
877 if (!DateOrTime || MenuOption->Sequence == 2) {\r
878 PrintCharAt ((UINTN)-1, (UINTN)-1, RIGHT_NUMERIC_DELIMITER);\r
879 }\r
880 }\r
881\r
882 goto EnterCarriageReturn;\r
7c6c064c
ED
883\r
884 case SCAN_UP:\r
885 case SCAN_DOWN:\r
886 goto EnterCarriageReturn;\r
887\r
888 case SCAN_ESC:\r
889 return EFI_DEVICE_ERROR;\r
890\r
891 default:\r
892 break;\r
893 }\r
894\r
895 break;\r
896\r
897EnterCarriageReturn:\r
898\r
899 case CHAR_CARRIAGE_RETURN:\r
900 //\r
901 // Validate input value with Minimum value.\r
902 //\r
afc9bf79
ED
903 ValidateFail = FALSE;\r
904 if (IntInput) {\r
905 //\r
906 // After user input Enter, need to check whether the input value.\r
907 // If input a negative value, should compare with maximum value. \r
908 // else compare with the minimum value.\r
909 //\r
910 if (Negative) {\r
911 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;\r
912 } else {\r
913 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;\r
914 }\r
915\r
916 if (ValidateFail) {\r
917 UpdateStatusBar (INPUT_ERROR, TRUE);\r
918 break;\r
919 }\r
920 } else if (EditValue < Minimum) {\r
7c6c064c
ED
921 UpdateStatusBar (INPUT_ERROR, TRUE);\r
922 break;\r
7c6c064c 923 }\r
afc9bf79
ED
924\r
925 UpdateStatusBar (INPUT_ERROR, FALSE);\r
7c6c064c
ED
926 CopyMem (&gUserInput->InputValue, &Question->CurrentValue, sizeof (EFI_HII_VALUE));\r
927 QuestionValue = &gUserInput->InputValue;\r
928 //\r
929 // Store Edit value back to Question\r
930 //\r
931 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP) {\r
932 switch (MenuOption->Sequence) {\r
933 case 0:\r
934 QuestionValue->Value.date.Month = (UINT8) EditValue;\r
935 break;\r
936\r
937 case 1:\r
938 QuestionValue->Value.date.Day = (UINT8) EditValue;\r
939 break;\r
940\r
941 case 2:\r
942 QuestionValue->Value.date.Year = (UINT16) EditValue;\r
943 break;\r
944\r
945 default:\r
946 break;\r
947 }\r
948 } else if (Question->OpCode->OpCode == EFI_IFR_TIME_OP) {\r
949 switch (MenuOption->Sequence) {\r
950 case 0:\r
951 QuestionValue->Value.time.Hour = (UINT8) EditValue;\r
952 break;\r
953\r
954 case 1:\r
955 QuestionValue->Value.time.Minute = (UINT8) EditValue;\r
956 break;\r
957\r
958 case 2:\r
959 QuestionValue->Value.time.Second = (UINT8) EditValue;\r
960 break;\r
961\r
962 default:\r
963 break;\r
964 }\r
965 } else {\r
966 //\r
967 // Numeric\r
968 //\r
969 QuestionValue->Value.u64 = EditValue;\r
970 }\r
971\r
972 //\r
973 // Adjust the value to the correct one.\r
974 // Sample like: 2012.02.29 -> 2013.02.29 -> 2013.02.01\r
975 // 2013.03.29 -> 2013.02.29 -> 2013.02.28\r
976 //\r
977 if (Question->OpCode->OpCode == EFI_IFR_DATE_OP && \r
978 (MenuOption->Sequence == 0 || MenuOption->Sequence == 2)) {\r
979 AdjustQuestionValue (QuestionValue, (UINT8)MenuOption->Sequence);\r
980 }\r
981\r
bfae1330 982 return EFI_SUCCESS;\r
7c6c064c
ED
983\r
984 case CHAR_BACKSPACE:\r
985 if (ManualInput) {\r
986 if (Count == 0) {\r
afc9bf79
ED
987 if (Negative) {\r
988 Negative = FALSE;\r
989 Column--;\r
990 PrintStringAt (Column, Row, L" ");\r
991 }\r
7c6c064c
ED
992 break;\r
993 }\r
994 //\r
995 // Remove a character\r
996 //\r
997 EditValue = PreviousNumber[Count - 1];\r
998 UpdateStatusBar (INPUT_ERROR, FALSE);\r
999 Count--;\r
1000 Column--;\r
1001 PrintStringAt (Column, Row, L" ");\r
1002 }\r
1003 break;\r
1004\r
1005 default:\r
1006 if (ManualInput) {\r
1007 if (HexInput) {\r
1008 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {\r
1009 Digital = (UINT8) (Key.UnicodeChar - L'0');\r
1010 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {\r
1011 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);\r
1012 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {\r
1013 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);\r
1014 } else {\r
1015 UpdateStatusBar (INPUT_ERROR, TRUE);\r
1016 break;\r
1017 }\r
1018 } else {\r
1019 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {\r
1020 UpdateStatusBar (INPUT_ERROR, TRUE);\r
1021 break;\r
1022 }\r
1023 }\r
1024\r
1025 //\r
1026 // If Count exceed input width, there is no way more is valid\r
1027 //\r
1028 if (Count >= InputWidth) {\r
1029 break;\r
1030 }\r
1031 //\r
1032 // Someone typed something valid!\r
1033 //\r
1034 if (Count != 0) {\r
1035 if (HexInput) {\r
1036 EditValue = LShiftU64 (EditValue, 4) + Digital;\r
afc9bf79
ED
1037 } else if (IntInput && Negative) {\r
1038 //\r
1039 // Save the negative number.\r
1040 //\r
1041 EditValue = ~(MultU64x32 (~(EditValue - 1), 10) + (Key.UnicodeChar - L'0')) + 1;\r
7c6c064c
ED
1042 } else {\r
1043 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');\r
1044 }\r
1045 } else {\r
1046 if (HexInput) {\r
1047 EditValue = Digital;\r
afc9bf79
ED
1048 } else if (IntInput && Negative) {\r
1049 //\r
1050 // Save the negative number.\r
1051 //\r
1052 EditValue = ~(Key.UnicodeChar - L'0') + 1;\r
7c6c064c
ED
1053 } else {\r
1054 EditValue = Key.UnicodeChar - L'0';\r
1055 }\r
1056 }\r
1057\r
afc9bf79
ED
1058 if (IntInput) {\r
1059 ValidateFail = FALSE;\r
1060 //\r
1061 // When user input a new value, should check the current value.\r
1062 // If user input a negative value, should compare it with minimum\r
1063 // value, else compare it with maximum value.\r
1064 //\r
1065 if (Negative) {\r
1066 ValidateFail = (INT64) EditValue < (INT64) Minimum ? TRUE : FALSE;\r
1067 } else {\r
1068 ValidateFail = (INT64) EditValue > (INT64) Maximum ? TRUE : FALSE;\r
1069 }\r
1070\r
1071 if (ValidateFail) {\r
1072 UpdateStatusBar (INPUT_ERROR, TRUE);\r
1073 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));\r
1074 EditValue = PreviousNumber[Count];\r
1075 break;\r
1076 }\r
7c6c064c 1077 } else {\r
afc9bf79
ED
1078 if (EditValue > Maximum) {\r
1079 UpdateStatusBar (INPUT_ERROR, TRUE);\r
1080 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));\r
1081 EditValue = PreviousNumber[Count];\r
1082 break;\r
1083 }\r
7c6c064c
ED
1084 }\r
1085\r
afc9bf79
ED
1086 UpdateStatusBar (INPUT_ERROR, FALSE);\r
1087\r
7c6c064c
ED
1088 Count++;\r
1089 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));\r
1090 PreviousNumber[Count] = EditValue;\r
1091\r
1092 gST->ConOut->SetAttribute (gST->ConOut, GetHighlightTextColor ());\r
1093 PrintCharAt (Column, Row, Key.UnicodeChar);\r
1094 Column++;\r
1095 }\r
1096 break;\r
1097 }\r
1098 } while (TRUE);\r
1099}\r
1100\r
1101/**\r
1102 Adjust option order base on the question value.\r
1103\r
1104 @param Question Pointer to current question.\r
1105 @param PopUpMenuLines The line number of the pop up menu.\r
1106\r
1107 @retval EFI_SUCCESS If Option input is processed successfully\r
1108 @retval EFI_DEVICE_ERROR If operation fails\r
1109\r
1110**/\r
1111EFI_STATUS\r
1112AdjustOptionOrder (\r
1113 IN FORM_DISPLAY_ENGINE_STATEMENT *Question,\r
1114 OUT UINTN *PopUpMenuLines\r
1115 )\r
1116{\r
1117 UINTN Index;\r
1118 EFI_IFR_ORDERED_LIST *OrderList;\r
1119 UINT8 *ValueArray;\r
1120 UINT8 ValueType;\r
1121 LIST_ENTRY *Link;\r
1122 DISPLAY_QUESTION_OPTION *OneOfOption;\r
1123 EFI_HII_VALUE *HiiValueArray;\r
1124\r
1125 Link = GetFirstNode (&Question->OptionListHead);\r
1126 OneOfOption = DISPLAY_QUESTION_OPTION_FROM_LINK (Link);\r
1127 ValueArray = Question->CurrentValue.Buffer;\r
1128 ValueType = OneOfOption->OptionOpCode->Type;\r
1129 OrderList = (EFI_IFR_ORDERED_LIST *) Question->OpCode;\r
1130\r
1131 for (Index = 0; Index < OrderList->MaxContainers; Index++) {\r
1132 if (GetArrayData (ValueArray, ValueType, Index) == 0) {\r
1133 break;\r
1134 }\r
1135 }\r
1136 \r
1137 *PopUpMenuLines = Index;\r
1138 \r
1139 //\r
1140 // Prepare HiiValue array\r
1141 // \r
1142 HiiValueArray = AllocateZeroPool (*PopUpMenuLines * sizeof (EFI_HII_VALUE));\r
1143 ASSERT (HiiValueArray != NULL);\r
1144\r
1145 for (Index = 0; Index < *PopUpMenuLines; Index++) {\r
1146 HiiValueArray[Index].Type = ValueType;\r
1147 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
1148 }\r
1149 \r
1150 for (Index = 0; Index < *PopUpMenuLines; Index++) {\r
1151 OneOfOption = ValueToOption (Question, &HiiValueArray[*PopUpMenuLines - Index - 1]);\r
1152 if (OneOfOption == NULL) {\r
1153 return EFI_NOT_FOUND;\r
1154 }\r
1155 \r
1156 RemoveEntryList (&OneOfOption->Link);\r
1157 \r
1158 //\r
1159 // Insert to head.\r
1160 //\r
1161 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
1162 }\r
1163 \r
1164 FreePool (HiiValueArray);\r
1165\r
1166 return EFI_SUCCESS;\r
1167}\r
1168\r
1169/**\r
1170 Base on the type to compare the value.\r
1171\r
1172 @param Value1 The first value need to compare.\r
1173 @param Value2 The second value need to compare.\r
1174 @param Type The value type for above two values.\r
1175\r
1176 @retval TRUE The two value are same.\r
1177 @retval FALSE The two value are different.\r
1178\r
1179**/\r
1180BOOLEAN\r
1181IsValuesEqual (\r
1182 IN EFI_IFR_TYPE_VALUE *Value1,\r
1183 IN EFI_IFR_TYPE_VALUE *Value2,\r
1184 IN UINT8 Type\r
1185 )\r
1186{\r
1187 switch (Type) {\r
1188 case EFI_IFR_TYPE_BOOLEAN:\r
1189 case EFI_IFR_TYPE_NUM_SIZE_8:\r
5a9f73bf 1190 return (BOOLEAN) (Value1->u8 == Value2->u8);\r
7c6c064c
ED
1191 \r
1192 case EFI_IFR_TYPE_NUM_SIZE_16:\r
5a9f73bf
ED
1193 return (BOOLEAN) (Value1->u16 == Value2->u16);\r
1194 \r
7c6c064c 1195 case EFI_IFR_TYPE_NUM_SIZE_32:\r
5a9f73bf
ED
1196 return (BOOLEAN) (Value1->u32 == Value2->u32);\r
1197 \r
7c6c064c 1198 case EFI_IFR_TYPE_NUM_SIZE_64:\r
5a9f73bf 1199 return (BOOLEAN) (Value1->u64 == Value2->u64);\r
7c6c064c
ED
1200\r
1201 default:\r
1202 ASSERT (FALSE);\r
1203 return FALSE;\r
1204 }\r
1205}\r
1206\r
1207/**\r
1208 Base on the type to set the value.\r
1209\r
1210 @param Dest The dest value.\r
1211 @param Source The source value.\r
1212 @param Type The value type for above two values.\r
1213\r
1214**/\r
1215VOID\r
1216SetValuesByType (\r
1217 OUT EFI_IFR_TYPE_VALUE *Dest,\r
1218 IN EFI_IFR_TYPE_VALUE *Source,\r
1219 IN UINT8 Type\r
1220 )\r
1221{\r
1222 switch (Type) {\r
1223 case EFI_IFR_TYPE_BOOLEAN:\r
1224 Dest->b = Source->b;\r
1225 break;\r
1226\r
1227 case EFI_IFR_TYPE_NUM_SIZE_8:\r
1228 Dest->u8 = Source->u8;\r
1229 break;\r
1230\r
1231 case EFI_IFR_TYPE_NUM_SIZE_16:\r
1232 Dest->u16 = Source->u16;\r
1233 break;\r
1234\r
1235 case EFI_IFR_TYPE_NUM_SIZE_32:\r
1236 Dest->u32 = Source->u32;\r
1237 break;\r
1238\r
1239 case EFI_IFR_TYPE_NUM_SIZE_64:\r
1240 Dest->u64 = Source->u64;\r
1241 break;\r
1242\r
1243 default:\r
1244 ASSERT (FALSE);\r
1245 break;\r
1246 }\r
1247}\r
1248\r
1249/**\r
1250 Get selection for OneOf and OrderedList (Left/Right will be ignored).\r
1251\r
1252 @param MenuOption Pointer to the current input menu.\r
1253\r
1254 @retval EFI_SUCCESS If Option input is processed successfully\r
1255 @retval EFI_DEVICE_ERROR If operation fails\r
1256\r
1257**/\r
1258EFI_STATUS\r
1259GetSelectionInputPopUp (\r
1260 IN UI_MENU_OPTION *MenuOption\r
1261 )\r
1262{\r
7c6c064c
ED
1263 EFI_INPUT_KEY Key;\r
1264 UINTN Index;\r
1265 CHAR16 *StringPtr;\r
1266 CHAR16 *TempStringPtr;\r
1267 UINTN Index2;\r
1268 UINTN TopOptionIndex;\r
1269 UINTN HighlightOptionIndex;\r
1270 UINTN Start;\r
1271 UINTN End;\r
1272 UINTN Top;\r
1273 UINTN Bottom;\r
1274 UINTN PopUpMenuLines;\r
1275 UINTN MenuLinesInView;\r
1276 UINTN PopUpWidth;\r
1277 CHAR16 Character;\r
1278 INT32 SavedAttribute;\r
1279 BOOLEAN ShowDownArrow;\r
1280 BOOLEAN ShowUpArrow;\r
1281 UINTN DimensionsWidth;\r
1282 LIST_ENTRY *Link;\r
1283 BOOLEAN OrderedList;\r
1284 UINT8 *ValueArray;\r
1285 UINT8 *ReturnValue;\r
1286 UINT8 ValueType;\r
1287 EFI_HII_VALUE HiiValue;\r
1288 DISPLAY_QUESTION_OPTION *OneOfOption;\r
1289 DISPLAY_QUESTION_OPTION *CurrentOption;\r
1290 FORM_DISPLAY_ENGINE_STATEMENT *Question;\r
1291 INTN Result;\r
1292 EFI_IFR_ORDERED_LIST *OrderList;\r
1293\r
1294 DimensionsWidth = gStatementDimensions.RightColumn - gStatementDimensions.LeftColumn;\r
1295\r
1296 ValueArray = NULL;\r
1297 ValueType = 0;\r
1298 CurrentOption = NULL;\r
1299 ShowDownArrow = FALSE;\r
1300 ShowUpArrow = FALSE;\r
1301\r
1302 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
1303 ASSERT (StringPtr);\r
1304\r
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
7c6c064c
ED
1663 \r
1664 default:\r
1665 break;\r
1666 }\r
1667 } while (TRUE);\r
1668\r
1669}\r
1670\r