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