]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
1. Update SetupBrowser to pass correct buffer value for OrderedList Callback
[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
d02847d3 4Copyright (c) 2004 - 2009, Intel Corporation\r
7936fb6a 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
8b0fc5c1 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
d02847d3 710 UINT8 ValueType;\r
7936fb6a 711 EFI_HII_VALUE HiiValue;\r
712 EFI_HII_VALUE *HiiValueArray;\r
713 UINTN OptionCount;\r
714 QUESTION_OPTION *OneOfOption;\r
715 QUESTION_OPTION *CurrentOption;\r
716 FORM_BROWSER_STATEMENT *Question;\r
717\r
718 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
719\r
720 ValueArray = NULL;\r
d02847d3 721 ValueType = 0;\r
7936fb6a 722 CurrentOption = NULL;\r
723 ShowDownArrow = FALSE;\r
724 ShowUpArrow = FALSE;\r
725\r
726 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
727 ASSERT (StringPtr);\r
728\r
729 Question = MenuOption->ThisTag;\r
730 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {\r
731 ValueArray = Question->BufferValue;\r
b5a906f4 732 ValueType = Question->ValueType;\r
7936fb6a 733 OrderedList = TRUE;\r
734 } else {\r
735 OrderedList = FALSE;\r
736 }\r
737\r
738 //\r
739 // Calculate Option count\r
740 //\r
741 if (OrderedList) {\r
742 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
d02847d3 743 if (GetArrayData (ValueArray, ValueType, Index) == 0) {\r
7936fb6a 744 break;\r
745 }\r
746 }\r
747\r
748 OptionCount = Index;\r
749 } else {\r
750 OptionCount = 0;\r
751 Link = GetFirstNode (&Question->OptionListHead);\r
752 while (!IsNull (&Question->OptionListHead, Link)) {\r
753 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
754\r
755 OptionCount++;\r
756\r
757 Link = GetNextNode (&Question->OptionListHead, Link);\r
758 }\r
759 }\r
760\r
761 //\r
762 // Prepare HiiValue array\r
763 //\r
764 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));\r
765 ASSERT (HiiValueArray != NULL);\r
766 Link = GetFirstNode (&Question->OptionListHead);\r
767 for (Index = 0; Index < OptionCount; Index++) {\r
768 if (OrderedList) {\r
d02847d3 769 HiiValueArray[Index].Type = ValueType;\r
770 HiiValueArray[Index].Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
7936fb6a 771 } else {\r
772 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
773 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));\r
774 Link = GetNextNode (&Question->OptionListHead, Link);\r
775 }\r
776 }\r
777\r
778 //\r
779 // Move Suppressed Option to list tail\r
780 //\r
781 PopUpMenuLines = 0;\r
782 for (Index = 0; Index < OptionCount; Index++) {\r
783 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);\r
784 if (OneOfOption == NULL) {\r
785 return EFI_NOT_FOUND;\r
786 }\r
787\r
788 RemoveEntryList (&OneOfOption->Link);\r
789\r
790 if ((OneOfOption->SuppressExpression != NULL) &&\r
791 (OneOfOption->SuppressExpression->Result.Value.b)) {\r
792 //\r
793 // This option is suppressed, insert to tail\r
794 //\r
795 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
796 } else {\r
797 //\r
798 // Insert to head\r
799 //\r
800 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
801\r
802 PopUpMenuLines++;\r
803 }\r
804 }\r
805\r
806 //\r
807 // Get the number of one of options present and its size\r
808 //\r
809 PopUpWidth = 0;\r
810 HighlightOptionIndex = 0;\r
811 Link = GetFirstNode (&Question->OptionListHead);\r
812 for (Index = 0; Index < PopUpMenuLines; Index++) {\r
813 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
814\r
815 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
816 if (StrLen (StringPtr) > PopUpWidth) {\r
817 PopUpWidth = StrLen (StringPtr);\r
818 }\r
f4113e1f 819 FreePool (StringPtr);\r
7936fb6a 820\r
821 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {\r
822 //\r
823 // Find current selected Option for OneOf\r
824 //\r
825 HighlightOptionIndex = Index;\r
826 }\r
827\r
828 Link = GetNextNode (&Question->OptionListHead, Link);\r
829 }\r
830\r
831 //\r
832 // Perform popup menu initialization.\r
833 //\r
834 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
835\r
836 SavedAttribute = gST->ConOut->Mode->Attribute;\r
837 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
838\r
839 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
840 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
841 }\r
842\r
843 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;\r
844 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
845 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;\r
846 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;\r
847\r
848 MenuLinesInView = Bottom - Top - 1;\r
849 if (MenuLinesInView >= PopUpMenuLines) {\r
850 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
851 Bottom = Top + PopUpMenuLines + 1;\r
852 } else {\r
853 ShowDownArrow = TRUE;\r
854 }\r
855\r
856 if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
857 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
858 } else {\r
859 TopOptionIndex = 0;\r
860 }\r
861\r
862 do {\r
863 //\r
864 // Clear that portion of the screen\r
865 //\r
866 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);\r
867\r
868 //\r
869 // Draw "One of" pop-up menu\r
870 //\r
871 Character = BOXDRAW_DOWN_RIGHT;\r
872 PrintCharAt (Start, Top, Character);\r
873 for (Index = Start; Index + 2 < End; Index++) {\r
874 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
875 Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
876 } else {\r
877 Character = BOXDRAW_HORIZONTAL;\r
878 }\r
879\r
880 PrintChar (Character);\r
881 }\r
882\r
883 Character = BOXDRAW_DOWN_LEFT;\r
884 PrintChar (Character);\r
885 Character = BOXDRAW_VERTICAL;\r
886 for (Index = Top + 1; Index < Bottom; Index++) {\r
887 PrintCharAt (Start, Index, Character);\r
888 PrintCharAt (End - 1, Index, Character);\r
889 }\r
890\r
891 //\r
892 // Move to top Option\r
893 //\r
894 Link = GetFirstNode (&Question->OptionListHead);\r
895 for (Index = 0; Index < TopOptionIndex; Index++) {\r
896 Link = GetNextNode (&Question->OptionListHead, Link);\r
897 }\r
898\r
899 //\r
900 // Display the One of options\r
901 //\r
902 Index2 = Top + 1;\r
903 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
904 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
905 Link = GetNextNode (&Question->OptionListHead, Link);\r
906\r
907 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
908 //\r
909 // If the string occupies multiple lines, truncate it to fit in one line,\r
910 // and append a "..." for indication.\r
911 //\r
912 if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
913 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
914 ASSERT ( TempStringPtr != NULL );\r
915 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
f4113e1f 916 FreePool (StringPtr);\r
7936fb6a 917 StringPtr = TempStringPtr;\r
918 StrCat (StringPtr, L"...");\r
919 }\r
920\r
921 if (Index == HighlightOptionIndex) {\r
922 //\r
923 // Highlight the selected one\r
924 //\r
925 CurrentOption = OneOfOption;\r
926\r
927 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);\r
928 PrintStringAt (Start + 2, Index2, StringPtr);\r
929 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
930 } else {\r
931 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
932 PrintStringAt (Start + 2, Index2, StringPtr);\r
933 }\r
934\r
935 Index2++;\r
f4113e1f 936 FreePool (StringPtr);\r
7936fb6a 937 }\r
938\r
939 Character = BOXDRAW_UP_RIGHT;\r
940 PrintCharAt (Start, Bottom, Character);\r
941 for (Index = Start; Index + 2 < End; Index++) {\r
942 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
943 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
944 } else {\r
945 Character = BOXDRAW_HORIZONTAL;\r
946 }\r
947\r
948 PrintChar (Character);\r
949 }\r
950\r
951 Character = BOXDRAW_UP_LEFT;\r
952 PrintChar (Character);\r
953\r
954 //\r
955 // Get User selection\r
956 //\r
957 Key.UnicodeChar = CHAR_NULL;\r
958 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
959 Key.ScanCode = gDirection;\r
960 gDirection = 0;\r
961 goto TheKey;\r
962 }\r
963\r
964 Status = WaitForKeyStroke (&Key);\r
965\r
966TheKey:\r
967 switch (Key.UnicodeChar) {\r
968 case '+':\r
969 if (OrderedList) {\r
970 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
971 //\r
972 // Highlight reaches the top of the popup window, scroll one menu item.\r
973 //\r
974 TopOptionIndex--;\r
975 ShowDownArrow = TRUE;\r
976 }\r
977\r
978 if (TopOptionIndex == 0) {\r
979 ShowUpArrow = FALSE;\r
980 }\r
981\r
982 if (HighlightOptionIndex > 0) {\r
983 HighlightOptionIndex--;\r
984\r
40a06b0c 985 ASSERT (CurrentOption != NULL);\r
7936fb6a 986 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
987 }\r
988 }\r
989 break;\r
990\r
991 case '-':\r
992 //\r
993 // If an ordered list op-code, we will allow for a popup of +/- keys\r
994 // to create an ordered list of items\r
995 //\r
996 if (OrderedList) {\r
997 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
998 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
999 //\r
1000 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1001 //\r
1002 TopOptionIndex++;\r
1003 ShowUpArrow = TRUE;\r
1004 }\r
1005\r
1006 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1007 ShowDownArrow = FALSE;\r
1008 }\r
1009\r
1010 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1011 HighlightOptionIndex++;\r
1012\r
40a06b0c 1013 ASSERT (CurrentOption != NULL);\r
7936fb6a 1014 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
1015 }\r
1016 }\r
1017 break;\r
1018\r
1019 case CHAR_NULL:\r
1020 switch (Key.ScanCode) {\r
1021 case SCAN_UP:\r
1022 case SCAN_DOWN:\r
1023 if (Key.ScanCode == SCAN_UP) {\r
1024 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1025 //\r
1026 // Highlight reaches the top of the popup window, scroll one menu item.\r
1027 //\r
1028 TopOptionIndex--;\r
1029 ShowDownArrow = TRUE;\r
1030 }\r
1031\r
1032 if (TopOptionIndex == 0) {\r
1033 ShowUpArrow = FALSE;\r
1034 }\r
1035\r
1036 if (HighlightOptionIndex > 0) {\r
1037 HighlightOptionIndex--;\r
1038 }\r
1039 } else {\r
1040 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1041 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1042 //\r
1043 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1044 //\r
1045 TopOptionIndex++;\r
1046 ShowUpArrow = TRUE;\r
1047 }\r
1048\r
1049 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1050 ShowDownArrow = FALSE;\r
1051 }\r
1052\r
1053 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1054 HighlightOptionIndex++;\r
1055 }\r
1056 }\r
1057 break;\r
1058\r
1059 case SCAN_ESC:\r
1060 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1061\r
1062 //\r
1063 // Restore link list order for orderedlist\r
1064 //\r
1065 if (OrderedList) {\r
d02847d3 1066 HiiValue.Type = ValueType;\r
7936fb6a 1067 HiiValue.Value.u64 = 0;\r
1068 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
d02847d3 1069 HiiValue.Value.u64 = GetArrayData (ValueArray, ValueType, Index);\r
1070 if (HiiValue.Value.u64 == 0) {\r
7936fb6a 1071 break;\r
1072 }\r
1073\r
1074 OneOfOption = ValueToOption (Question, &HiiValue);\r
1075 if (OneOfOption == NULL) {\r
1076 return EFI_NOT_FOUND;\r
1077 }\r
1078\r
1079 RemoveEntryList (&OneOfOption->Link);\r
1080 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
1081 }\r
1082 }\r
1083\r
f4113e1f 1084 FreePool (HiiValueArray);\r
7936fb6a 1085 return EFI_DEVICE_ERROR;\r
1086\r
1087 default:\r
1088 break;\r
1089 }\r
1090\r
1091 break;\r
1092\r
1093 case CHAR_CARRIAGE_RETURN:\r
1094 //\r
1095 // return the current selection\r
1096 //\r
1097 if (OrderedList) {\r
1098 Index = 0;\r
1099 Link = GetFirstNode (&Question->OptionListHead);\r
1100 while (!IsNull (&Question->OptionListHead, Link)) {\r
1101 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
1102\r
d02847d3 1103 SetArrayData (ValueArray, ValueType, Index, OneOfOption->Value.Value.u64);\r
7936fb6a 1104\r
1105 Index++;\r
1106 if (Index > Question->MaxContainers) {\r
1107 break;\r
1108 }\r
1109\r
1110 Link = GetNextNode (&Question->OptionListHead, Link);\r
1111 }\r
1112 } else {\r
40a06b0c 1113 ASSERT (CurrentOption != NULL);\r
7936fb6a 1114 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));\r
1115 }\r
1116\r
1117 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
f4113e1f 1118 FreePool (HiiValueArray);\r
7936fb6a 1119\r
1120 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
1121 if (EFI_ERROR (Status)) {\r
1122 //\r
1123 // Input value is not valid, restore Question Value\r
1124 //\r
1125 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1126 } else {\r
1127 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1128 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
1129 }\r
1130\r
1131 return Status;\r
1132\r
1133 default:\r
1134 break;\r
1135 }\r
1136 } while (TRUE);\r
1137\r
1138}\r
1139\r
1140/**\r
1141 Wait for a key to be pressed by user.\r
1142\r
1143 @param Key The key which is pressed by user.\r
1144\r
1145 @retval EFI_SUCCESS The function always completed successfully.\r
1146\r
1147**/\r
1148EFI_STATUS\r
1149WaitForKeyStroke (\r
1150 OUT EFI_INPUT_KEY *Key\r
1151 )\r
1152{\r
1153 EFI_STATUS Status;\r
1154\r
1155 do {\r
1156 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);\r
1157 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
1158 } while (EFI_ERROR(Status));\r
1159\r
1160 return Status;\r
1161}\r