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