]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
Update the conflicted function name with UefiLib from CreatePopUp to CreateMultiStrin...
[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
3ebb9bdb 95 CreateMultiStringPopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);\r
7936fb6a 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
f4113e1f 117 FreePool (TempString);\r
118 FreePool (BufferedString);\r
7936fb6a 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
f4113e1f 132 FreePool (TempString);\r
133 FreePool (BufferedString);\r
7936fb6a 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
f4113e1f 146 FreePool (TempString);\r
147 FreePool (BufferedString);\r
7936fb6a 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
40a06b0c 238 CHAR16 InputText[MAX_NUMERIC_INPUT_WIDTH];\r
239 CHAR16 FormattedNumber[MAX_NUMERIC_INPUT_WIDTH - 1];\r
240 UINT64 PreviousNumber[MAX_NUMERIC_INPUT_WIDTH - 3];\r
7936fb6a 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
bc166db3 390 ASSERT (InputWidth + 2 < MAX_NUMERIC_INPUT_WIDTH); \r
7936fb6a 391 InputText[InputWidth + 1] = RIGHT_NUMERIC_DELIMITER;\r
392 InputText[InputWidth + 2] = L'\0';\r
393\r
394 PrintAt (Column, Row, InputText);\r
395 Column++;\r
396 }\r
397\r
398 //\r
399 // First time we enter this handler, we need to check to see if\r
400 // we were passed an increment or decrement directive\r
401 //\r
402 do {\r
403 Key.UnicodeChar = CHAR_NULL;\r
404 if (gDirection != 0) {\r
405 Key.ScanCode = gDirection;\r
406 gDirection = 0;\r
407 goto TheKey2;\r
408 }\r
409\r
410 Status = WaitForKeyStroke (&Key);\r
411\r
412TheKey2:\r
413 switch (Key.UnicodeChar) {\r
414\r
415 case '+':\r
416 case '-':\r
417 if (Key.UnicodeChar == '+') {\r
418 Key.ScanCode = SCAN_RIGHT;\r
419 } else {\r
420 Key.ScanCode = SCAN_LEFT;\r
421 }\r
422 Key.UnicodeChar = CHAR_NULL;\r
423 goto TheKey2;\r
424\r
425 case CHAR_NULL:\r
426 switch (Key.ScanCode) {\r
427 case SCAN_LEFT:\r
428 case SCAN_RIGHT:\r
429 if (DateOrTime) {\r
430 //\r
431 // By setting this value, we will return back to the caller.\r
432 // We need to do this since an auto-refresh will destroy the adjustment\r
433 // based on what the real-time-clock is showing. So we always commit\r
434 // upon changing the value.\r
435 //\r
436 gDirection = SCAN_DOWN;\r
437 }\r
438\r
439 if (!ManualInput) {\r
440 if (Key.ScanCode == SCAN_LEFT) {\r
441 if (EditValue > Step) {\r
442 EditValue = EditValue - Step;\r
443 } else {\r
444 EditValue = Minimum;\r
445 }\r
446 } else if (Key.ScanCode == SCAN_RIGHT) {\r
447 EditValue = EditValue + Step;\r
448 if (EditValue > Maximum) {\r
449 EditValue = Maximum;\r
450 }\r
451 }\r
452\r
453 ZeroMem (FormattedNumber, 21 * sizeof (CHAR16));\r
454 if (Question->Operand == EFI_IFR_DATE_OP) {\r
455 if (MenuOption->Sequence == 2) {\r
456 //\r
457 // Year\r
458 //\r
459 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%04d", EditValue);\r
460 } else {\r
461 //\r
462 // Month/Day\r
463 //\r
464 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);\r
465 }\r
466\r
467 if (MenuOption->Sequence == 0) {\r
468 FormattedNumber[EraseLen - 2] = DATE_SEPARATOR;\r
469 } else if (MenuOption->Sequence == 1) {\r
470 FormattedNumber[EraseLen - 1] = DATE_SEPARATOR;\r
471 }\r
472 } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
473 UnicodeSPrint (FormattedNumber, 21 * sizeof (CHAR16), L"%02d", EditValue);\r
474\r
475 if (MenuOption->Sequence == 0) {\r
476 FormattedNumber[EraseLen - 2] = TIME_SEPARATOR;\r
477 } else if (MenuOption->Sequence == 1) {\r
478 FormattedNumber[EraseLen - 1] = TIME_SEPARATOR;\r
479 }\r
480 } else {\r
481 QuestionValue->Value.u64 = EditValue;\r
482 PrintFormattedNumber (Question, FormattedNumber, 21 * sizeof (CHAR16));\r
483 }\r
484\r
485 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);\r
486 for (Loop = 0; Loop < EraseLen; Loop++) {\r
487 PrintAt (MenuOption->OptCol + Loop, MenuOption->Row, L" ");\r
488 }\r
489 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);\r
490\r
491 if (MenuOption->Sequence == 0) {\r
492 PrintCharAt (MenuOption->OptCol, Row, LEFT_NUMERIC_DELIMITER);\r
493 Column = MenuOption->OptCol + 1;\r
494 }\r
495\r
496 PrintStringAt (Column, Row, FormattedNumber);\r
497\r
498 if (!DateOrTime || MenuOption->Sequence == 2) {\r
499 PrintChar (RIGHT_NUMERIC_DELIMITER);\r
500 }\r
501 }\r
e8e36190 502\r
503 goto EnterCarriageReturn;\r
7936fb6a 504 break;\r
505\r
506 case SCAN_UP:\r
507 case SCAN_DOWN:\r
508 goto EnterCarriageReturn;\r
509\r
510 case SCAN_ESC:\r
511 return EFI_DEVICE_ERROR;\r
512\r
513 default:\r
514 break;\r
515 }\r
516\r
517 break;\r
518\r
519EnterCarriageReturn:\r
520\r
521 case CHAR_CARRIAGE_RETURN:\r
522 //\r
523 // Store Edit value back to Question\r
524 //\r
525 if (Question->Operand == EFI_IFR_DATE_OP) {\r
526 switch (MenuOption->Sequence) {\r
527 case 0:\r
528 QuestionValue->Value.date.Month = (UINT8) EditValue;\r
529 break;\r
530\r
531 case 1:\r
532 QuestionValue->Value.date.Day = (UINT8) EditValue;\r
533 break;\r
534\r
535 case 2:\r
536 QuestionValue->Value.date.Year = (UINT16) EditValue;\r
537 break;\r
538\r
539 default:\r
540 break;\r
541 }\r
542 } else if (Question->Operand == EFI_IFR_TIME_OP) {\r
543 switch (MenuOption->Sequence) {\r
544 case 0:\r
545 QuestionValue->Value.time.Hour = (UINT8) EditValue;\r
546 break;\r
547\r
548 case 1:\r
549 QuestionValue->Value.time.Minute = (UINT8) EditValue;\r
550 break;\r
551\r
552 case 2:\r
553 QuestionValue->Value.time.Second = (UINT8) EditValue;\r
554 break;\r
555\r
556 default:\r
557 break;\r
558 }\r
559 } else {\r
560 //\r
561 // Numeric\r
562 //\r
563 QuestionValue->Value.u64 = EditValue;\r
564 }\r
565\r
566 //\r
567 // Check to see if the Value is something reasonable against consistency limitations.\r
568 // If not, let's kick the error specified.\r
569 //\r
570 Status = ValidateQuestion (FormSet, Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
571 if (EFI_ERROR (Status)) {\r
572 //\r
573 // Input value is not valid, restore Question Value\r
574 //\r
575 GetQuestionValue (FormSet, Form, Question, TRUE);\r
576 } else {\r
577 SetQuestionValue (FormSet, Form, Question, TRUE);\r
578 if (!DateOrTime || (Question->Storage != NULL)) {\r
579 //\r
580 // NV flag is unnecessary for RTC type of Date/Time\r
581 //\r
582 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
583 }\r
584 }\r
585\r
586 return Status;\r
587 break;\r
588\r
589 case CHAR_BACKSPACE:\r
590 if (ManualInput) {\r
591 if (Count == 0) {\r
592 break;\r
593 }\r
594 //\r
595 // Remove a character\r
596 //\r
597 EditValue = PreviousNumber[Count - 1];\r
598 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);\r
599 Count--;\r
600 Column--;\r
601 PrintAt (Column, Row, L" ");\r
602 }\r
603 break;\r
604\r
605 default:\r
606 if (ManualInput) {\r
607 if (HexInput) {\r
63d55bb9
LG
608 if ((Key.UnicodeChar >= L'0') && (Key.UnicodeChar <= L'9')) {\r
609 Digital = (UINT8) (Key.UnicodeChar - L'0');\r
610 } else if ((Key.UnicodeChar >= L'A') && (Key.UnicodeChar <= L'F')) {\r
611 Digital = (UINT8) (Key.UnicodeChar - L'A' + 0x0A);\r
612 } else if ((Key.UnicodeChar >= L'a') && (Key.UnicodeChar <= L'f')) {\r
613 Digital = (UINT8) (Key.UnicodeChar - L'a' + 0x0A);\r
614 } else {\r
7936fb6a 615 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
616 break;\r
617 }\r
618 } else {\r
619 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {\r
620 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
621 break;\r
622 }\r
623 }\r
624\r
625 //\r
626 // If Count exceed input width, there is no way more is valid\r
627 //\r
628 if (Count >= InputWidth) {\r
629 break;\r
630 }\r
631 //\r
632 // Someone typed something valid!\r
633 //\r
634 if (Count != 0) {\r
635 if (HexInput) {\r
636 EditValue = LShiftU64 (EditValue, 4) + Digital;\r
637 } else {\r
638 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');\r
639 }\r
640 } else {\r
641 if (HexInput) {\r
642 EditValue = Digital;\r
643 } else {\r
644 EditValue = Key.UnicodeChar - L'0';\r
645 }\r
646 }\r
647\r
648 if (EditValue > Maximum) {\r
649 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
40a06b0c 650 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));\r
7936fb6a 651 EditValue = PreviousNumber[Count];\r
652 break;\r
653 } else {\r
654 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);\r
655 }\r
656\r
657 Count++;\r
bc166db3 658 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));\r
7936fb6a 659 PreviousNumber[Count] = EditValue;\r
660\r
661 PrintCharAt (Column, Row, Key.UnicodeChar);\r
662 Column++;\r
663 }\r
664 break;\r
665 }\r
666 } while (TRUE);\r
667\r
668}\r
669\r
670\r
671/**\r
672 Get selection for OneOf and OrderedList (Left/Right will be ignored).\r
673\r
674 @param Selection Pointer to current selection.\r
675 @param MenuOption Pointer to the current input menu.\r
676\r
677 @retval EFI_SUCCESS If Option input is processed successfully\r
678 @retval EFI_DEVICE_ERROR If operation fails\r
679\r
680**/\r
681EFI_STATUS\r
682GetSelectionInputPopUp (\r
683 IN UI_MENU_SELECTION *Selection,\r
684 IN UI_MENU_OPTION *MenuOption\r
685 )\r
686{\r
687 EFI_STATUS Status;\r
688 EFI_INPUT_KEY Key;\r
689 UINTN Index;\r
690 CHAR16 *StringPtr;\r
691 CHAR16 *TempStringPtr;\r
692 UINTN Index2;\r
693 UINTN TopOptionIndex;\r
694 UINTN HighlightOptionIndex;\r
695 UINTN Start;\r
696 UINTN End;\r
697 UINTN Top;\r
698 UINTN Bottom;\r
699 UINTN PopUpMenuLines;\r
700 UINTN MenuLinesInView;\r
701 UINTN PopUpWidth;\r
702 CHAR16 Character;\r
703 INT32 SavedAttribute;\r
704 BOOLEAN ShowDownArrow;\r
705 BOOLEAN ShowUpArrow;\r
706 UINTN DimensionsWidth;\r
707 LIST_ENTRY *Link;\r
708 BOOLEAN OrderedList;\r
709 UINT8 *ValueArray;\r
710 EFI_HII_VALUE HiiValue;\r
711 EFI_HII_VALUE *HiiValueArray;\r
712 UINTN OptionCount;\r
713 QUESTION_OPTION *OneOfOption;\r
714 QUESTION_OPTION *CurrentOption;\r
715 FORM_BROWSER_STATEMENT *Question;\r
716\r
717 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
718\r
719 ValueArray = NULL;\r
720 CurrentOption = NULL;\r
721 ShowDownArrow = FALSE;\r
722 ShowUpArrow = FALSE;\r
723\r
724 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
725 ASSERT (StringPtr);\r
726\r
727 Question = MenuOption->ThisTag;\r
728 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {\r
729 ValueArray = Question->BufferValue;\r
730 OrderedList = TRUE;\r
731 } else {\r
732 OrderedList = FALSE;\r
733 }\r
734\r
735 //\r
736 // Calculate Option count\r
737 //\r
738 if (OrderedList) {\r
739 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
740 if (ValueArray[Index] == 0) {\r
741 break;\r
742 }\r
743 }\r
744\r
745 OptionCount = Index;\r
746 } else {\r
747 OptionCount = 0;\r
748 Link = GetFirstNode (&Question->OptionListHead);\r
749 while (!IsNull (&Question->OptionListHead, Link)) {\r
750 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
751\r
752 OptionCount++;\r
753\r
754 Link = GetNextNode (&Question->OptionListHead, Link);\r
755 }\r
756 }\r
757\r
758 //\r
759 // Prepare HiiValue array\r
760 //\r
761 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));\r
762 ASSERT (HiiValueArray != NULL);\r
763 Link = GetFirstNode (&Question->OptionListHead);\r
764 for (Index = 0; Index < OptionCount; Index++) {\r
765 if (OrderedList) {\r
766 HiiValueArray[Index].Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
767 HiiValueArray[Index].Value.u8 = ValueArray[Index];\r
768 } else {\r
769 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
770 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));\r
771 Link = GetNextNode (&Question->OptionListHead, Link);\r
772 }\r
773 }\r
774\r
775 //\r
776 // Move Suppressed Option to list tail\r
777 //\r
778 PopUpMenuLines = 0;\r
779 for (Index = 0; Index < OptionCount; Index++) {\r
780 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);\r
781 if (OneOfOption == NULL) {\r
782 return EFI_NOT_FOUND;\r
783 }\r
784\r
785 RemoveEntryList (&OneOfOption->Link);\r
786\r
787 if ((OneOfOption->SuppressExpression != NULL) &&\r
788 (OneOfOption->SuppressExpression->Result.Value.b)) {\r
789 //\r
790 // This option is suppressed, insert to tail\r
791 //\r
792 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
793 } else {\r
794 //\r
795 // Insert to head\r
796 //\r
797 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
798\r
799 PopUpMenuLines++;\r
800 }\r
801 }\r
802\r
803 //\r
804 // Get the number of one of options present and its size\r
805 //\r
806 PopUpWidth = 0;\r
807 HighlightOptionIndex = 0;\r
808 Link = GetFirstNode (&Question->OptionListHead);\r
809 for (Index = 0; Index < PopUpMenuLines; Index++) {\r
810 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
811\r
812 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
813 if (StrLen (StringPtr) > PopUpWidth) {\r
814 PopUpWidth = StrLen (StringPtr);\r
815 }\r
f4113e1f 816 FreePool (StringPtr);\r
7936fb6a 817\r
818 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {\r
819 //\r
820 // Find current selected Option for OneOf\r
821 //\r
822 HighlightOptionIndex = Index;\r
823 }\r
824\r
825 Link = GetNextNode (&Question->OptionListHead, Link);\r
826 }\r
827\r
828 //\r
829 // Perform popup menu initialization.\r
830 //\r
831 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
832\r
833 SavedAttribute = gST->ConOut->Mode->Attribute;\r
834 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
835\r
836 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
837 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
838 }\r
839\r
840 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;\r
841 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
842 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;\r
843 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;\r
844\r
845 MenuLinesInView = Bottom - Top - 1;\r
846 if (MenuLinesInView >= PopUpMenuLines) {\r
847 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
848 Bottom = Top + PopUpMenuLines + 1;\r
849 } else {\r
850 ShowDownArrow = TRUE;\r
851 }\r
852\r
853 if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
854 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
855 } else {\r
856 TopOptionIndex = 0;\r
857 }\r
858\r
859 do {\r
860 //\r
861 // Clear that portion of the screen\r
862 //\r
863 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);\r
864\r
865 //\r
866 // Draw "One of" pop-up menu\r
867 //\r
868 Character = BOXDRAW_DOWN_RIGHT;\r
869 PrintCharAt (Start, Top, Character);\r
870 for (Index = Start; Index + 2 < End; Index++) {\r
871 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
872 Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
873 } else {\r
874 Character = BOXDRAW_HORIZONTAL;\r
875 }\r
876\r
877 PrintChar (Character);\r
878 }\r
879\r
880 Character = BOXDRAW_DOWN_LEFT;\r
881 PrintChar (Character);\r
882 Character = BOXDRAW_VERTICAL;\r
883 for (Index = Top + 1; Index < Bottom; Index++) {\r
884 PrintCharAt (Start, Index, Character);\r
885 PrintCharAt (End - 1, Index, Character);\r
886 }\r
887\r
888 //\r
889 // Move to top Option\r
890 //\r
891 Link = GetFirstNode (&Question->OptionListHead);\r
892 for (Index = 0; Index < TopOptionIndex; Index++) {\r
893 Link = GetNextNode (&Question->OptionListHead, Link);\r
894 }\r
895\r
896 //\r
897 // Display the One of options\r
898 //\r
899 Index2 = Top + 1;\r
900 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
901 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
902 Link = GetNextNode (&Question->OptionListHead, Link);\r
903\r
904 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
905 //\r
906 // If the string occupies multiple lines, truncate it to fit in one line,\r
907 // and append a "..." for indication.\r
908 //\r
909 if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
910 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
911 ASSERT ( TempStringPtr != NULL );\r
912 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
f4113e1f 913 FreePool (StringPtr);\r
7936fb6a 914 StringPtr = TempStringPtr;\r
915 StrCat (StringPtr, L"...");\r
916 }\r
917\r
918 if (Index == HighlightOptionIndex) {\r
919 //\r
920 // Highlight the selected one\r
921 //\r
922 CurrentOption = OneOfOption;\r
923\r
924 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);\r
925 PrintStringAt (Start + 2, Index2, StringPtr);\r
926 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
927 } else {\r
928 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
929 PrintStringAt (Start + 2, Index2, StringPtr);\r
930 }\r
931\r
932 Index2++;\r
f4113e1f 933 FreePool (StringPtr);\r
7936fb6a 934 }\r
935\r
936 Character = BOXDRAW_UP_RIGHT;\r
937 PrintCharAt (Start, Bottom, Character);\r
938 for (Index = Start; Index + 2 < End; Index++) {\r
939 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
940 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
941 } else {\r
942 Character = BOXDRAW_HORIZONTAL;\r
943 }\r
944\r
945 PrintChar (Character);\r
946 }\r
947\r
948 Character = BOXDRAW_UP_LEFT;\r
949 PrintChar (Character);\r
950\r
951 //\r
952 // Get User selection\r
953 //\r
954 Key.UnicodeChar = CHAR_NULL;\r
955 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
956 Key.ScanCode = gDirection;\r
957 gDirection = 0;\r
958 goto TheKey;\r
959 }\r
960\r
961 Status = WaitForKeyStroke (&Key);\r
962\r
963TheKey:\r
964 switch (Key.UnicodeChar) {\r
965 case '+':\r
966 if (OrderedList) {\r
967 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
968 //\r
969 // Highlight reaches the top of the popup window, scroll one menu item.\r
970 //\r
971 TopOptionIndex--;\r
972 ShowDownArrow = TRUE;\r
973 }\r
974\r
975 if (TopOptionIndex == 0) {\r
976 ShowUpArrow = FALSE;\r
977 }\r
978\r
979 if (HighlightOptionIndex > 0) {\r
980 HighlightOptionIndex--;\r
981\r
40a06b0c 982 ASSERT (CurrentOption != NULL);\r
7936fb6a 983 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
984 }\r
985 }\r
986 break;\r
987\r
988 case '-':\r
989 //\r
990 // If an ordered list op-code, we will allow for a popup of +/- keys\r
991 // to create an ordered list of items\r
992 //\r
993 if (OrderedList) {\r
994 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
995 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
996 //\r
997 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
998 //\r
999 TopOptionIndex++;\r
1000 ShowUpArrow = TRUE;\r
1001 }\r
1002\r
1003 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1004 ShowDownArrow = FALSE;\r
1005 }\r
1006\r
1007 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1008 HighlightOptionIndex++;\r
1009\r
40a06b0c 1010 ASSERT (CurrentOption != NULL);\r
7936fb6a 1011 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
1012 }\r
1013 }\r
1014 break;\r
1015\r
1016 case CHAR_NULL:\r
1017 switch (Key.ScanCode) {\r
1018 case SCAN_UP:\r
1019 case SCAN_DOWN:\r
1020 if (Key.ScanCode == SCAN_UP) {\r
1021 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1022 //\r
1023 // Highlight reaches the top of the popup window, scroll one menu item.\r
1024 //\r
1025 TopOptionIndex--;\r
1026 ShowDownArrow = TRUE;\r
1027 }\r
1028\r
1029 if (TopOptionIndex == 0) {\r
1030 ShowUpArrow = FALSE;\r
1031 }\r
1032\r
1033 if (HighlightOptionIndex > 0) {\r
1034 HighlightOptionIndex--;\r
1035 }\r
1036 } else {\r
1037 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1038 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1039 //\r
1040 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1041 //\r
1042 TopOptionIndex++;\r
1043 ShowUpArrow = TRUE;\r
1044 }\r
1045\r
1046 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1047 ShowDownArrow = FALSE;\r
1048 }\r
1049\r
1050 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1051 HighlightOptionIndex++;\r
1052 }\r
1053 }\r
1054 break;\r
1055\r
1056 case SCAN_ESC:\r
1057 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1058\r
1059 //\r
1060 // Restore link list order for orderedlist\r
1061 //\r
1062 if (OrderedList) {\r
1063 HiiValue.Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
1064 HiiValue.Value.u64 = 0;\r
1065 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
1066 HiiValue.Value.u8 = ValueArray[Index];\r
d1a54e2c 1067 if (HiiValue.Value.u8 != 0) {\r
7936fb6a 1068 break;\r
1069 }\r
1070\r
1071 OneOfOption = ValueToOption (Question, &HiiValue);\r
1072 if (OneOfOption == NULL) {\r
1073 return EFI_NOT_FOUND;\r
1074 }\r
1075\r
1076 RemoveEntryList (&OneOfOption->Link);\r
1077 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
1078 }\r
1079 }\r
1080\r
f4113e1f 1081 FreePool (HiiValueArray);\r
7936fb6a 1082 return EFI_DEVICE_ERROR;\r
1083\r
1084 default:\r
1085 break;\r
1086 }\r
1087\r
1088 break;\r
1089\r
1090 case CHAR_CARRIAGE_RETURN:\r
1091 //\r
1092 // return the current selection\r
1093 //\r
1094 if (OrderedList) {\r
1095 Index = 0;\r
1096 Link = GetFirstNode (&Question->OptionListHead);\r
1097 while (!IsNull (&Question->OptionListHead, Link)) {\r
1098 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
1099\r
1100 Question->BufferValue[Index] = OneOfOption->Value.Value.u8;\r
1101\r
1102 Index++;\r
1103 if (Index > Question->MaxContainers) {\r
1104 break;\r
1105 }\r
1106\r
1107 Link = GetNextNode (&Question->OptionListHead, Link);\r
1108 }\r
1109 } else {\r
40a06b0c 1110 ASSERT (CurrentOption != NULL);\r
7936fb6a 1111 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));\r
1112 }\r
1113\r
1114 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
f4113e1f 1115 FreePool (HiiValueArray);\r
7936fb6a 1116\r
1117 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
1118 if (EFI_ERROR (Status)) {\r
1119 //\r
1120 // Input value is not valid, restore Question Value\r
1121 //\r
1122 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1123 } else {\r
1124 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1125 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
1126 }\r
1127\r
1128 return Status;\r
1129\r
1130 default:\r
1131 break;\r
1132 }\r
1133 } while (TRUE);\r
1134\r
1135}\r
1136\r
1137/**\r
1138 Wait for a key to be pressed by user.\r
1139\r
1140 @param Key The key which is pressed by user.\r
1141\r
1142 @retval EFI_SUCCESS The function always completed successfully.\r
1143\r
1144**/\r
1145EFI_STATUS\r
1146WaitForKeyStroke (\r
1147 OUT EFI_INPUT_KEY *Key\r
1148 )\r
1149{\r
1150 EFI_STATUS Status;\r
1151\r
1152 do {\r
1153 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);\r
1154 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
1155 } while (EFI_ERROR(Status));\r
1156\r
1157 return Status;\r
1158}\r