]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
1) Move RFC_3066_ENTRY_SIZE and ISO_639_2_ENTRY_SIZE to UefiBaseType.h.
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / InputHandler.c
CommitLineData
7936fb6a 1/** @file\r
2Implementation for handling user input from the User Interfaces.\r
3\r
4Copyright (c) 2004 - 2007, Intel Corporation\r
5All rights reserved. This 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 "Ui.h"\r
16#include "Setup.h"\r
17\r
18\r
19/**\r
20 Get string or password input from user.\r
21\r
22 @param MenuOption Pointer to the current input menu.\r
23 @param Prompt The prompt string shown on popup window.\r
24 @param StringPtr Destination for use input string.\r
25\r
26 @retval EFI_SUCCESS If string input is read successfully\r
27 @retval EFI_DEVICE_ERROR If operation fails\r
28\r
29**/\r
30EFI_STATUS\r
31ReadString (\r
32 IN UI_MENU_OPTION *MenuOption,\r
33 IN CHAR16 *Prompt,\r
34 OUT CHAR16 *StringPtr\r
35 )\r
36{\r
37 EFI_STATUS Status;\r
38 EFI_INPUT_KEY Key;\r
39 CHAR16 NullCharacter;\r
40 UINTN ScreenSize;\r
41 CHAR16 Space[2];\r
42 CHAR16 KeyPad[2];\r
43 CHAR16 *TempString;\r
44 CHAR16 *BufferedString;\r
45 UINTN Index;\r
46 UINTN Count;\r
47 UINTN Start;\r
48 UINTN Top;\r
49 UINTN DimensionsWidth;\r
50 UINTN DimensionsHeight;\r
51 BOOLEAN CursorVisible;\r
52 UINTN Minimum;\r
53 UINTN Maximum;\r
54 FORM_BROWSER_STATEMENT *Question;\r
55 BOOLEAN IsPassword;\r
56\r
57 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
58 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;\r
59\r
60 NullCharacter = CHAR_NULL;\r
61 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);\r
62 Space[0] = L' ';\r
63 Space[1] = CHAR_NULL;\r
64\r
65 Question = MenuOption->ThisTag;\r
66 Minimum = (UINTN) Question->Minimum;\r
67 Maximum = (UINTN) Question->Maximum;\r
68\r
69 if (Question->Operand == EFI_IFR_PASSWORD_OP) {\r
70 IsPassword = TRUE;\r
71 } else {\r
72 IsPassword = FALSE;\r
73 }\r
74\r
75 TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));\r
76 ASSERT (TempString);\r
77\r
78 if (ScreenSize < (Maximum + 1)) {\r
79 ScreenSize = Maximum + 1;\r
80 }\r
81\r
82 if ((ScreenSize + 2) > DimensionsWidth) {\r
83 ScreenSize = DimensionsWidth - 2;\r
84 }\r
85\r
86 BufferedString = AllocateZeroPool (ScreenSize * 2);\r
87 ASSERT (BufferedString);\r
88\r
89 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;\r
90 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;\r
91\r
92 //\r
93 // Display prompt for string\r
94 //\r
95 CreatePopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);\r
96\r
97 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
98\r
99 CursorVisible = gST->ConOut->Mode->CursorVisible;\r
100 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
101\r
102 do {\r
103 Status = WaitForKeyStroke (&Key);\r
104 ASSERT_EFI_ERROR (Status);\r
105\r
106 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
107 switch (Key.UnicodeChar) {\r
108 case CHAR_NULL:\r
109 switch (Key.ScanCode) {\r
110 case SCAN_LEFT:\r
111 break;\r
112\r
113 case SCAN_RIGHT:\r
114 break;\r
115\r
116 case SCAN_ESC:\r
117 gBS->FreePool (TempString);\r
118 gBS->FreePool (BufferedString);\r
119 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
120 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
121 return EFI_DEVICE_ERROR;\r
122\r
123 default:\r
124 break;\r
125 }\r
126\r
127 break;\r
128\r
129 case CHAR_CARRIAGE_RETURN:\r
130 if (GetStringWidth (StringPtr) >= ((Minimum + 1) * sizeof (CHAR16))) {\r
131\r
132 gBS->FreePool (TempString);\r
133 gBS->FreePool (BufferedString);\r
134 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
135 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
136 return EFI_SUCCESS;\r
137 } else {\r
138 //\r
139 // Simply create a popup to tell the user that they had typed in too few characters.\r
140 // To save code space, we can then treat this as an error and return back to the menu.\r
141 //\r
142 do {\r
143 CreateDialog (4, TRUE, 0, NULL, &Key, &NullCharacter, gMiniString, gPressEnter, &NullCharacter);\r
144 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);\r
145\r
146 gBS->FreePool (TempString);\r
147 gBS->FreePool (BufferedString);\r
148 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
149 gST->ConOut->EnableCursor (gST->ConOut, CursorVisible);\r
150 return EFI_DEVICE_ERROR;\r
151 }\r
152\r
153 break;\r
154\r
155 case CHAR_BACKSPACE:\r
156 if (StringPtr[0] != CHAR_NULL) {\r
157 for (Index = 0; StringPtr[Index] != CHAR_NULL; Index++) {\r
158 TempString[Index] = StringPtr[Index];\r
159 }\r
160 //\r
161 // Effectively truncate string by 1 character\r
162 //\r
163 TempString[Index - 1] = CHAR_NULL;\r
164 StrCpy (StringPtr, TempString);\r
165 }\r
166\r
167 default:\r
168 //\r
169 // If it is the beginning of the string, don't worry about checking maximum limits\r
170 //\r
171 if ((StringPtr[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
172 StrnCpy (StringPtr, &Key.UnicodeChar, 1);\r
173 StrnCpy (TempString, &Key.UnicodeChar, 1);\r
174 } else if ((GetStringWidth (StringPtr) < ((Maximum + 1) * sizeof (CHAR16))) && (Key.UnicodeChar != CHAR_BACKSPACE)) {\r
175 KeyPad[0] = Key.UnicodeChar;\r
176 KeyPad[1] = CHAR_NULL;\r
177 StrCat (StringPtr, KeyPad);\r
178 StrCat (TempString, KeyPad);\r
179 }\r
180\r
181 //\r
182 // If the width of the input string is now larger than the screen, we nee to\r
183 // adjust the index to start printing portions of the string\r
184 //\r
185 SetUnicodeMem (BufferedString, ScreenSize - 1, L' ');\r
186 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
187\r
188 if ((GetStringWidth (StringPtr) / 2) > (DimensionsWidth - 2)) {\r
189 Index = (GetStringWidth (StringPtr) / 2) - DimensionsWidth + 2;\r
190 } else {\r
191 Index = 0;\r
192 }\r
193\r
194 if (IsPassword) {\r
195 gST->ConOut->SetCursorPosition (gST->ConOut, Start + 1, Top + 3);\r
196 }\r
197\r
198 for (Count = 0; Index + 1 < GetStringWidth (StringPtr) / 2; Index++, Count++) {\r
199 BufferedString[Count] = StringPtr[Index];\r
200\r
201 if (IsPassword) {\r
202 PrintChar (L'*');\r
203 }\r
204 }\r
205\r
206 if (!IsPassword) {\r
207 PrintStringAt (Start + 1, Top + 3, BufferedString);\r
208 }\r
209 break;\r
210 }\r
211\r
212 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));\r
213 gST->ConOut->SetCursorPosition (gST->ConOut, Start + GetStringWidth (StringPtr) / 2, Top + 3);\r
214 } while (TRUE);\r
215\r
216}\r
217\r
218\r
219/**\r
220 This routine reads a numeric value from the user input.\r
221\r
222 @param Selection Pointer to current selection.\r
223 @param MenuOption Pointer to the current input menu.\r
224\r
225 @retval EFI_SUCCESS If numerical input is read successfully\r
226 @retval EFI_DEVICE_ERROR If operation fails\r
227\r
228**/\r
229EFI_STATUS\r
230GetNumericInput (\r
231 IN UI_MENU_SELECTION *Selection,\r
232 IN UI_MENU_OPTION *MenuOption\r
233 )\r
234{\r
235 EFI_STATUS Status;\r
236 UINTN Column;\r
237 UINTN Row;\r
238 CHAR16 InputText[23];\r
239 CHAR16 FormattedNumber[22];\r
240 UINT64 PreviousNumber[20];\r
241 UINTN Count;\r
242 UINTN Loop;\r
243 BOOLEAN ManualInput;\r
244 BOOLEAN HexInput;\r
245 BOOLEAN DateOrTime;\r
246 UINTN InputWidth;\r
247 UINT64 EditValue;\r
248 UINT64 Step;\r
249 UINT64 Minimum;\r
250 UINT64 Maximum;\r
251 UINTN EraseLen;\r
252 UINT8 Digital;\r
253 EFI_INPUT_KEY Key;\r
254 EFI_HII_VALUE *QuestionValue;\r
255 FORM_BROWSER_FORM *Form;\r
256 FORM_BROWSER_FORMSET *FormSet;\r
257 FORM_BROWSER_STATEMENT *Question;\r
258\r
259 Column = MenuOption->OptCol;\r
260 Row = MenuOption->Row;\r
261 PreviousNumber[0] = 0;\r
262 Count = 0;\r
263 InputWidth = 0;\r
264 Digital = 0;\r
265\r
266 FormSet = Selection->FormSet;\r
267 Form = Selection->Form;\r
268 Question = MenuOption->ThisTag;\r
269 QuestionValue = &Question->HiiValue;\r
270 Step = Question->Step;\r
271 Minimum = Question->Minimum;\r
272 Maximum = Question->Maximum;\r
273\r
274 if ((Question->Operand == EFI_IFR_DATE_OP) || (Question->Operand == EFI_IFR_TIME_OP)) {\r
275 DateOrTime = TRUE;\r
276 } else {\r
277 DateOrTime = FALSE;\r
278 }\r
279\r
280 //\r
281 // Prepare Value to be edit\r
282 //\r
283 EraseLen = 0;\r
284 EditValue = 0;\r
285 if (Question->Operand == EFI_IFR_DATE_OP) {\r
286 Step = 1;\r
287 Minimum = 1;\r
288\r
289 switch (MenuOption->Sequence) {\r
290 case 0:\r
291 Maximum = 12;\r
292 EraseLen = 4;\r
293 EditValue = QuestionValue->Value.date.Month;\r
294 break;\r
295\r
296 case 1:\r
297 Maximum = 31;\r
298 EraseLen = 3;\r
299 EditValue = QuestionValue->Value.date.Day;\r
300 break;\r
301\r
302 case 2:\r
303 Maximum = 0xffff;\r
304 EraseLen = 5;\r
305 EditValue = QuestionValue->Value.date.Year;\r
306 break;\r
307\r
308 default:\r
309 break;\r
310 }\r
311 } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
312 Step = 1;\r
313 Minimum = 0;\r
314\r
315 switch (MenuOption->Sequence) {\r
316 case 0:\r
317 Maximum = 23;\r
318 EraseLen = 4;\r
319 EditValue = QuestionValue->Value.time.Hour;\r
320 break;\r
321\r
322 case 1:\r
323 Maximum = 59;\r
324 EraseLen = 3;\r
325 EditValue = QuestionValue->Value.time.Minute;\r
326 break;\r
327\r
328 case 2:\r
329 Maximum = 59;\r
330 EraseLen = 3;\r
331 EditValue = QuestionValue->Value.time.Second;\r
332 break;\r
333\r
334 default:\r
335 break;\r
336 }\r
337 } else {\r
338 //\r
339 // Numeric\r
340 //\r
341 EraseLen = gOptionBlockWidth;\r
342 EditValue = QuestionValue->Value.u64;\r
343 if (Maximum == 0) {\r
344 Maximum = (UINT64) -1;\r
345 }\r
346 }\r
347\r
348 if (Step == 0) {\r
349 ManualInput = TRUE;\r
350 } else {\r
351 ManualInput = FALSE;\r
352 }\r
353\r
354 if ((Question->Operand == EFI_IFR_NUMERIC_OP) &&\r
355 ((Question->Flags & EFI_IFR_DISPLAY) == EFI_IFR_DISPLAY_UINT_HEX)) {\r
356 HexInput = TRUE;\r
357 } else {\r
358 HexInput = FALSE;\r
359 }\r
360\r
361 if (ManualInput) {\r
362 if (HexInput) {\r
363 InputWidth = Question->StorageWidth * 2;\r
364 } else {\r
365 switch (Question->StorageWidth) {\r
366 case 1:\r
367 InputWidth = 3;\r
368 break;\r
369\r
370 case 2:\r
371 InputWidth = 5;\r
372 break;\r
373\r
374 case 4:\r
375 InputWidth = 10;\r
376 break;\r
377\r
378 case 8:\r
379 InputWidth = 20;\r
380 break;\r
381\r
382 default:\r
383 InputWidth = 0;\r
384 break;\r
385 }\r
386 }\r
387\r
388 InputText[0] = LEFT_NUMERIC_DELIMITER;\r
389 SetUnicodeMem (InputText + 1, InputWidth, L' ');\r
390 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
391 InputText[InputWidth + 2] = L'\0';\r
392\r
393 PrintAt (Column, Row, InputText);\r
394 Column++;\r
395 }\r
396\r
397 //\r
398 // First time we enter this handler, we need to check to see if\r
399 // we were passed an increment or decrement directive\r
400 //\r
401 do {\r
402 Key.UnicodeChar = CHAR_NULL;\r
403 if (gDirection != 0) {\r
404 Key.ScanCode = gDirection;\r
405 gDirection = 0;\r
406 goto TheKey2;\r
407 }\r
408\r
409 Status = WaitForKeyStroke (&Key);\r
410\r
411TheKey2:\r
412 switch (Key.UnicodeChar) {\r
413\r
414 case '+':\r
415 case '-':\r
416 if (Key.UnicodeChar == '+') {\r
417 Key.ScanCode = SCAN_RIGHT;\r
418 } else {\r
419 Key.ScanCode = SCAN_LEFT;\r
420 }\r
421 Key.UnicodeChar = CHAR_NULL;\r
422 goto TheKey2;\r
423\r
424 case CHAR_NULL:\r
425 switch (Key.ScanCode) {\r
426 case SCAN_LEFT:\r
427 case SCAN_RIGHT:\r
428 if (DateOrTime) {\r
429 //\r
430 // By setting this value, we will return back to the caller.\r
431 // We need to do this since an auto-refresh will destroy the adjustment\r
432 // based on what the real-time-clock is showing. So we always commit\r
433 // upon changing the value.\r
434 //\r
435 gDirection = SCAN_DOWN;\r
436 }\r
437\r
438 if (!ManualInput) {\r
439 if (Key.ScanCode == SCAN_LEFT) {\r
440 if (EditValue > Step) {\r
441 EditValue = EditValue - Step;\r
442 } else {\r
443 EditValue = Minimum;\r
444 }\r
445 } else if (Key.ScanCode == SCAN_RIGHT) {\r
446 EditValue = EditValue + Step;\r
447 if (EditValue > Maximum) {\r
448 EditValue = Maximum;\r
449 }\r
450 }\r
451\r
452 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
453 if (Question->Operand == EFI_IFR_DATE_OP) {\r
454 if (MenuOption->Sequence == 2) {\r
455 //\r
456 // Year\r
457 //\r
458 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", EditValue);\r
459 } else {\r
460 //\r
461 // Month/Day\r
462 //\r
463 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);\r
464 }\r
465\r
466 if (MenuOption->Sequence == 0) {\r
467 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;\r
468 } else if (MenuOption->Sequence == 1) {\r
469 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;\r
470 }\r
471 } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
472 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);\r
473\r
474 if (MenuOption->Sequence == 0) {\r
475 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;\r
476 } else if (MenuOption->Sequence == 1) {\r
477 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;\r
478 }\r
479 } else {\r
480 QuestionValue->Value.u64 = EditValue;\r
481 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
482 }\r
483\r
484 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);\r
485 for (Loop = 0; Loop < EraseLen; Loop++) {\r
486 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");\r
487 }\r
488 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);\r
489\r
490 if (MenuOption->Sequence == 0) {\r
491 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);\r
492 Column = MenuOption->OptCol + 1;\r
493 }\r
494\r
495 PrintStringAt (Column, Row, FormattedNumber);\r
496\r
497 if (!DateOrTime || MenuOption->Sequence == 2) {\r
498 PrintChar (RIGHT_NUMERIC_DELIMITER);\r
499 }\r
500 }\r
e8e36190 501\r
502 goto EnterCarriageReturn;\r
7936fb6a 503 break;\r
504\r
505 case SCAN_UP:\r
506 case SCAN_DOWN:\r
507 goto EnterCarriageReturn;\r
508\r
509 case SCAN_ESC:\r
510 return EFI_DEVICE_ERROR;\r
511\r
512 default:\r
513 break;\r
514 }\r
515\r
516 break;\r
517\r
518EnterCarriageReturn:\r
519\r
520 case CHAR_CARRIAGE_RETURN:\r
521 //\r
522 // Store Edit value back to Question\r
523 //\r
524 if (Question->Operand == EFI_IFR_DATE_OP) {\r
525 switch (MenuOption->Sequence) {\r
526 case 0:\r
527 QuestionValue->Value.date.Month = (UINT8) EditValue;\r
528 break;\r
529\r
530 case 1:\r
531 QuestionValue->Value.date.Day = (UINT8) EditValue;\r
532 break;\r
533\r
534 case 2:\r
535 QuestionValue->Value.date.Year = (UINT16) EditValue;\r
536 break;\r
537\r
538 default:\r
539 break;\r
540 }\r
541 } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
542 switch (MenuOption->Sequence) {\r
543 case 0:\r
544 QuestionValue->Value.time.Hour = (UINT8) EditValue;\r
545 break;\r
546\r
547 case 1:\r
548 QuestionValue->Value.time.Minute = (UINT8) EditValue;\r
549 break;\r
550\r
551 case 2:\r
552 QuestionValue->Value.time.Second = (UINT8) EditValue;\r
553 break;\r
554\r
555 default:\r
556 break;\r
557 }\r
558 } else {\r
559 //\r
560 // Numeric\r
561 //\r
562 QuestionValue->Value.u64 = EditValue;\r
563 }\r
564\r
565 //\r
566 // Check to see if the Value is something reasonable against consistency limitations.\r
567 // If not, let's kick the error specified.\r
568 //\r
569 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
570 if (EFI_ERROR (Status)) {\r
571 //\r
572 // Input value is not valid, restore Question Value\r
573 //\r
574 GetQuestionValue (FormSet, Form, Question, TRUE);\r
575 } else {\r
576 SetQuestionValue (FormSet, Form, Question, TRUE);\r
577 if (!DateOrTime || (Question->Storage != NULL)) {\r
578 //\r
579 // NV flag is unnecessary for RTC type of Date/Time\r
580 //\r
581 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
582 }\r
583 }\r
584\r
585 return Status;\r
586 break;\r
587\r
588 case CHAR_BACKSPACE:\r
589 if (ManualInput) {\r
590 if (Count == 0) {\r
591 break;\r
592 }\r
593 //\r
594 // Remove a character\r
595 //\r
596 EditValue = PreviousNumber[Count - 1];\r
597 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);\r
598 Count--;\r
599 Column--;\r
600 PrintAt (Column, Row, L" ");\r
601 }\r
602 break;\r
603\r
604 default:\r
605 if (ManualInput) {\r
606 if (HexInput) {\r
607 if (!IsHexDigit (&Digital, Key.UnicodeChar)) {\r
608 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
609 break;\r
610 }\r
611 } else {\r
612 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {\r
613 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
614 break;\r
615 }\r
616 }\r
617\r
618 //\r
619 // If Count exceed input width, there is no way more is valid\r
620 //\r
621 if (Count >= InputWidth) {\r
622 break;\r
623 }\r
624 //\r
625 // Someone typed something valid!\r
626 //\r
627 if (Count != 0) {\r
628 if (HexInput) {\r
629 EditValue = LShiftU64 (EditValue, 4) + Digital;\r
630 } else {\r
631 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');\r
632 }\r
633 } else {\r
634 if (HexInput) {\r
635 EditValue = Digital;\r
636 } else {\r
637 EditValue = Key.UnicodeChar - L'0';\r
638 }\r
639 }\r
640\r
641 if (EditValue > Maximum) {\r
642 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
643 EditValue = PreviousNumber[Count];\r
644 break;\r
645 } else {\r
646 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);\r
647 }\r
648\r
649 Count++;\r
650 PreviousNumber[Count] = EditValue;\r
651\r
652 PrintCharAt (Column, Row, Key.UnicodeChar);\r
653 Column++;\r
654 }\r
655 break;\r
656 }\r
657 } while (TRUE);\r
658\r
659}\r
660\r
661\r
662/**\r
663 Get selection for OneOf and OrderedList (Left/Right will be ignored).\r
664\r
665 @param Selection Pointer to current selection.\r
666 @param MenuOption Pointer to the current input menu.\r
667\r
668 @retval EFI_SUCCESS If Option input is processed successfully\r
669 @retval EFI_DEVICE_ERROR If operation fails\r
670\r
671**/\r
672EFI_STATUS\r
673GetSelectionInputPopUp (\r
674 IN UI_MENU_SELECTION *Selection,\r
675 IN UI_MENU_OPTION *MenuOption\r
676 )\r
677{\r
678 EFI_STATUS Status;\r
679 EFI_INPUT_KEY Key;\r
680 UINTN Index;\r
681 CHAR16 *StringPtr;\r
682 CHAR16 *TempStringPtr;\r
683 UINTN Index2;\r
684 UINTN TopOptionIndex;\r
685 UINTN HighlightOptionIndex;\r
686 UINTN Start;\r
687 UINTN End;\r
688 UINTN Top;\r
689 UINTN Bottom;\r
690 UINTN PopUpMenuLines;\r
691 UINTN MenuLinesInView;\r
692 UINTN PopUpWidth;\r
693 CHAR16 Character;\r
694 INT32 SavedAttribute;\r
695 BOOLEAN ShowDownArrow;\r
696 BOOLEAN ShowUpArrow;\r
697 UINTN DimensionsWidth;\r
698 LIST_ENTRY *Link;\r
699 BOOLEAN OrderedList;\r
700 UINT8 *ValueArray;\r
701 EFI_HII_VALUE HiiValue;\r
702 EFI_HII_VALUE *HiiValueArray;\r
703 UINTN OptionCount;\r
704 QUESTION_OPTION *OneOfOption;\r
705 QUESTION_OPTION *CurrentOption;\r
706 FORM_BROWSER_STATEMENT *Question;\r
707\r
708 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
709\r
710 ValueArray = NULL;\r
711 CurrentOption = NULL;\r
712 ShowDownArrow = FALSE;\r
713 ShowUpArrow = FALSE;\r
714\r
715 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
716 ASSERT (StringPtr);\r
717\r
718 Question = MenuOption->ThisTag;\r
719 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {\r
720 ValueArray = Question->BufferValue;\r
721 OrderedList = TRUE;\r
722 } else {\r
723 OrderedList = FALSE;\r
724 }\r
725\r
726 //\r
727 // Calculate Option count\r
728 //\r
729 if (OrderedList) {\r
730 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
731 if (ValueArray[Index] == 0) {\r
732 break;\r
733 }\r
734 }\r
735\r
736 OptionCount = Index;\r
737 } else {\r
738 OptionCount = 0;\r
739 Link = GetFirstNode (&Question->OptionListHead);\r
740 while (!IsNull (&Question->OptionListHead, Link)) {\r
741 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
742\r
743 OptionCount++;\r
744\r
745 Link = GetNextNode (&Question->OptionListHead, Link);\r
746 }\r
747 }\r
748\r
749 //\r
750 // Prepare HiiValue array\r
751 //\r
752 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));\r
753 ASSERT (HiiValueArray != NULL);\r
754 Link = GetFirstNode (&Question->OptionListHead);\r
755 for (Index = 0; Index < OptionCount; Index++) {\r
756 if (OrderedList) {\r
757 HiiValueArray[Index].Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
758 HiiValueArray[Index].Value.u8 = ValueArray[Index];\r
759 } else {\r
760 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
761 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));\r
762 Link = GetNextNode (&Question->OptionListHead, Link);\r
763 }\r
764 }\r
765\r
766 //\r
767 // Move Suppressed Option to list tail\r
768 //\r
769 PopUpMenuLines = 0;\r
770 for (Index = 0; Index < OptionCount; Index++) {\r
771 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);\r
772 if (OneOfOption == NULL) {\r
773 return EFI_NOT_FOUND;\r
774 }\r
775\r
776 RemoveEntryList (&OneOfOption->Link);\r
777\r
778 if ((OneOfOption->SuppressExpression != NULL) &&\r
779 (OneOfOption->SuppressExpression->Result.Value.b)) {\r
780 //\r
781 // This option is suppressed, insert to tail\r
782 //\r
783 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
784 } else {\r
785 //\r
786 // Insert to head\r
787 //\r
788 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
789\r
790 PopUpMenuLines++;\r
791 }\r
792 }\r
793\r
794 //\r
795 // Get the number of one of options present and its size\r
796 //\r
797 PopUpWidth = 0;\r
798 HighlightOptionIndex = 0;\r
799 Link = GetFirstNode (&Question->OptionListHead);\r
800 for (Index = 0; Index < PopUpMenuLines; Index++) {\r
801 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
802\r
803 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
804 if (StrLen (StringPtr) > PopUpWidth) {\r
805 PopUpWidth = StrLen (StringPtr);\r
806 }\r
807 gBS->FreePool (StringPtr);\r
808\r
809 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {\r
810 //\r
811 // Find current selected Option for OneOf\r
812 //\r
813 HighlightOptionIndex = Index;\r
814 }\r
815\r
816 Link = GetNextNode (&Question->OptionListHead, Link);\r
817 }\r
818\r
819 //\r
820 // Perform popup menu initialization.\r
821 //\r
822 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
823\r
824 SavedAttribute = gST->ConOut->Mode->Attribute;\r
825 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
826\r
827 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
828 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
829 }\r
830\r
831 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;\r
832 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
833 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;\r
834 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;\r
835\r
836 MenuLinesInView = Bottom - Top - 1;\r
837 if (MenuLinesInView >= PopUpMenuLines) {\r
838 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
839 Bottom = Top + PopUpMenuLines + 1;\r
840 } else {\r
841 ShowDownArrow = TRUE;\r
842 }\r
843\r
844 if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
845 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
846 } else {\r
847 TopOptionIndex = 0;\r
848 }\r
849\r
850 do {\r
851 //\r
852 // Clear that portion of the screen\r
853 //\r
854 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);\r
855\r
856 //\r
857 // Draw "One of" pop-up menu\r
858 //\r
859 Character = BOXDRAW_DOWN_RIGHT;\r
860 PrintCharAt (Start, Top, Character);\r
861 for (Index = Start; Index + 2 < End; Index++) {\r
862 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
863 Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
864 } else {\r
865 Character = BOXDRAW_HORIZONTAL;\r
866 }\r
867\r
868 PrintChar (Character);\r
869 }\r
870\r
871 Character = BOXDRAW_DOWN_LEFT;\r
872 PrintChar (Character);\r
873 Character = BOXDRAW_VERTICAL;\r
874 for (Index = Top + 1; Index < Bottom; Index++) {\r
875 PrintCharAt (Start, Index, Character);\r
876 PrintCharAt (End - 1, Index, Character);\r
877 }\r
878\r
879 //\r
880 // Move to top Option\r
881 //\r
882 Link = GetFirstNode (&Question->OptionListHead);\r
883 for (Index = 0; Index < TopOptionIndex; Index++) {\r
884 Link = GetNextNode (&Question->OptionListHead, Link);\r
885 }\r
886\r
887 //\r
888 // Display the One of options\r
889 //\r
890 Index2 = Top + 1;\r
891 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
892 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
893 Link = GetNextNode (&Question->OptionListHead, Link);\r
894\r
895 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
896 //\r
897 // If the string occupies multiple lines, truncate it to fit in one line,\r
898 // and append a "..." for indication.\r
899 //\r
900 if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
901 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
902 ASSERT ( TempStringPtr != NULL );\r
903 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
904 gBS->FreePool (StringPtr);\r
905 StringPtr = TempStringPtr;\r
906 StrCat (StringPtr, L"...");\r
907 }\r
908\r
909 if (Index == HighlightOptionIndex) {\r
910 //\r
911 // Highlight the selected one\r
912 //\r
913 CurrentOption = OneOfOption;\r
914\r
915 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);\r
916 PrintStringAt (Start + 2, Index2, StringPtr);\r
917 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
918 } else {\r
919 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
920 PrintStringAt (Start + 2, Index2, StringPtr);\r
921 }\r
922\r
923 Index2++;\r
924 gBS->FreePool (StringPtr);\r
925 }\r
926\r
927 Character = BOXDRAW_UP_RIGHT;\r
928 PrintCharAt (Start, Bottom, Character);\r
929 for (Index = Start; Index + 2 < End; Index++) {\r
930 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
931 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
932 } else {\r
933 Character = BOXDRAW_HORIZONTAL;\r
934 }\r
935\r
936 PrintChar (Character);\r
937 }\r
938\r
939 Character = BOXDRAW_UP_LEFT;\r
940 PrintChar (Character);\r
941\r
942 //\r
943 // Get User selection\r
944 //\r
945 Key.UnicodeChar = CHAR_NULL;\r
946 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
947 Key.ScanCode = gDirection;\r
948 gDirection = 0;\r
949 goto TheKey;\r
950 }\r
951\r
952 Status = WaitForKeyStroke (&Key);\r
953\r
954TheKey:\r
955 switch (Key.UnicodeChar) {\r
956 case '+':\r
957 if (OrderedList) {\r
958 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
959 //\r
960 // Highlight reaches the top of the popup window, scroll one menu item.\r
961 //\r
962 TopOptionIndex--;\r
963 ShowDownArrow = TRUE;\r
964 }\r
965\r
966 if (TopOptionIndex == 0) {\r
967 ShowUpArrow = FALSE;\r
968 }\r
969\r
970 if (HighlightOptionIndex > 0) {\r
971 HighlightOptionIndex--;\r
972\r
973 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
974 }\r
975 }\r
976 break;\r
977\r
978 case '-':\r
979 //\r
980 // If an ordered list op-code, we will allow for a popup of +/- keys\r
981 // to create an ordered list of items\r
982 //\r
983 if (OrderedList) {\r
984 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
985 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
986 //\r
987 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
988 //\r
989 TopOptionIndex++;\r
990 ShowUpArrow = TRUE;\r
991 }\r
992\r
993 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
994 ShowDownArrow = FALSE;\r
995 }\r
996\r
997 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
998 HighlightOptionIndex++;\r
999\r
1000 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
1001 }\r
1002 }\r
1003 break;\r
1004\r
1005 case CHAR_NULL:\r
1006 switch (Key.ScanCode) {\r
1007 case SCAN_UP:\r
1008 case SCAN_DOWN:\r
1009 if (Key.ScanCode == SCAN_UP) {\r
1010 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1011 //\r
1012 // Highlight reaches the top of the popup window, scroll one menu item.\r
1013 //\r
1014 TopOptionIndex--;\r
1015 ShowDownArrow = TRUE;\r
1016 }\r
1017\r
1018 if (TopOptionIndex == 0) {\r
1019 ShowUpArrow = FALSE;\r
1020 }\r
1021\r
1022 if (HighlightOptionIndex > 0) {\r
1023 HighlightOptionIndex--;\r
1024 }\r
1025 } else {\r
1026 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1027 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1028 //\r
1029 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1030 //\r
1031 TopOptionIndex++;\r
1032 ShowUpArrow = TRUE;\r
1033 }\r
1034\r
1035 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1036 ShowDownArrow = FALSE;\r
1037 }\r
1038\r
1039 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1040 HighlightOptionIndex++;\r
1041 }\r
1042 }\r
1043 break;\r
1044\r
1045 case SCAN_ESC:\r
1046 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1047\r
1048 //\r
1049 // Restore link list order for orderedlist\r
1050 //\r
1051 if (OrderedList) {\r
1052 HiiValue.Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
1053 HiiValue.Value.u64 = 0;\r
1054 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
1055 HiiValue.Value.u8 = ValueArray[Index];\r
1056 if (HiiValue.Value.u8) {\r
1057 break;\r
1058 }\r
1059\r
1060 OneOfOption = ValueToOption (Question, &HiiValue);\r
1061 if (OneOfOption == NULL) {\r
1062 return EFI_NOT_FOUND;\r
1063 }\r
1064\r
1065 RemoveEntryList (&OneOfOption->Link);\r
1066 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
1067 }\r
1068 }\r
1069\r
1070 gBS->FreePool (HiiValueArray);\r
1071 return EFI_DEVICE_ERROR;\r
1072\r
1073 default:\r
1074 break;\r
1075 }\r
1076\r
1077 break;\r
1078\r
1079 case CHAR_CARRIAGE_RETURN:\r
1080 //\r
1081 // return the current selection\r
1082 //\r
1083 if (OrderedList) {\r
1084 Index = 0;\r
1085 Link = GetFirstNode (&Question->OptionListHead);\r
1086 while (!IsNull (&Question->OptionListHead, Link)) {\r
1087 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
1088\r
1089 Question->BufferValue[Index] = OneOfOption->Value.Value.u8;\r
1090\r
1091 Index++;\r
1092 if (Index > Question->MaxContainers) {\r
1093 break;\r
1094 }\r
1095\r
1096 Link = GetNextNode (&Question->OptionListHead, Link);\r
1097 }\r
1098 } else {\r
1099 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));\r
1100 }\r
1101\r
1102 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1103 gBS->FreePool (HiiValueArray);\r
1104\r
1105 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
1106 if (EFI_ERROR (Status)) {\r
1107 //\r
1108 // Input value is not valid, restore Question Value\r
1109 //\r
1110 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1111 } else {\r
1112 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1113 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
1114 }\r
1115\r
1116 return Status;\r
1117\r
1118 default:\r
1119 break;\r
1120 }\r
1121 } while (TRUE);\r
1122\r
1123}\r
1124\r
1125/**\r
1126 Wait for a key to be pressed by user.\r
1127\r
1128 @param Key The key which is pressed by user.\r
1129\r
1130 @retval EFI_SUCCESS The function always completed successfully.\r
1131\r
1132**/\r
1133EFI_STATUS\r
1134WaitForKeyStroke (\r
1135 OUT EFI_INPUT_KEY *Key\r
1136 )\r
1137{\r
1138 EFI_STATUS Status;\r
1139\r
1140 do {\r
1141 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);\r
1142 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
1143 } while (EFI_ERROR(Status));\r
1144\r
1145 return Status;\r
1146}\r