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