]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/SetupBrowserDxe/InputHandler.c
ECC cleanup: Non-Boolean comparisons should use a compare operator (==, !=, >, <...
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / InputHandler.c
CommitLineData
7936fb6a 1/** @file\r
2Implementation for handling user input from the User Interfaces.\r
3\r
4Copyright (c) 2004 - 2007, Intel Corporation\r
5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include "Ui.h"\r
16#include "Setup.h"\r
17\r
18\r
19/**\r
20 Get string or password input from user.\r
21\r
22 @param MenuOption Pointer to the current input menu.\r
23 @param Prompt The prompt string shown on popup window.\r
24 @param StringPtr Destination for use input string.\r
25\r
26 @retval EFI_SUCCESS If string input is read successfully\r
27 @retval EFI_DEVICE_ERROR If operation fails\r
28\r
29**/\r
30EFI_STATUS\r
31ReadString (\r
32 IN UI_MENU_OPTION *MenuOption,\r
33 IN CHAR16 *Prompt,\r
34 OUT CHAR16 *StringPtr\r
35 )\r
36{\r
37 EFI_STATUS Status;\r
38 EFI_INPUT_KEY Key;\r
39 CHAR16 NullCharacter;\r
40 UINTN ScreenSize;\r
41 CHAR16 Space[2];\r
42 CHAR16 KeyPad[2];\r
43 CHAR16 *TempString;\r
44 CHAR16 *BufferedString;\r
45 UINTN Index;\r
46 UINTN Count;\r
47 UINTN Start;\r
48 UINTN Top;\r
49 UINTN DimensionsWidth;\r
50 UINTN DimensionsHeight;\r
51 BOOLEAN CursorVisible;\r
52 UINTN Minimum;\r
53 UINTN Maximum;\r
54 FORM_BROWSER_STATEMENT *Question;\r
55 BOOLEAN IsPassword;\r
56\r
57 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
58 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;\r
59\r
60 NullCharacter = CHAR_NULL;\r
61 ScreenSize = GetStringWidth (Prompt) / sizeof (CHAR16);\r
62 Space[0] = L' ';\r
63 Space[1] = CHAR_NULL;\r
64\r
65 Question = MenuOption->ThisTag;\r
66 Minimum = (UINTN) Question->Minimum;\r
67 Maximum = (UINTN) Question->Maximum;\r
68\r
69 if (Question->Operand == EFI_IFR_PASSWORD_OP) {\r
70 IsPassword = TRUE;\r
71 } else {\r
72 IsPassword = FALSE;\r
73 }\r
74\r
75 TempString = AllocateZeroPool ((Maximum + 1)* sizeof (CHAR16));\r
76 ASSERT (TempString);\r
77\r
78 if (ScreenSize < (Maximum + 1)) {\r
79 ScreenSize = Maximum + 1;\r
80 }\r
81\r
82 if ((ScreenSize + 2) > DimensionsWidth) {\r
83 ScreenSize = DimensionsWidth - 2;\r
84 }\r
85\r
86 BufferedString = AllocateZeroPool (ScreenSize * 2);\r
87 ASSERT (BufferedString);\r
88\r
89 Start = (DimensionsWidth - ScreenSize - 2) / 2 + gScreenDimensions.LeftColumn + 1;\r
90 Top = ((DimensionsHeight - 6) / 2) + gScreenDimensions.TopRow - 1;\r
91\r
92 //\r
93 // Display prompt for string\r
94 //\r
95 CreatePopUp (ScreenSize, 4, &NullCharacter, Prompt, Space, &NullCharacter);\r
96\r
97 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
98\r
99 CursorVisible = gST->ConOut->Mode->CursorVisible;\r
100 gST->ConOut->EnableCursor (gST->ConOut, TRUE);\r
101\r
102 do {\r
103 Status = WaitForKeyStroke (&Key);\r
104 ASSERT_EFI_ERROR (Status);\r
105\r
106 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_BLACK, EFI_LIGHTGRAY));\r
107 switch (Key.UnicodeChar) {\r
108 case CHAR_NULL:\r
109 switch (Key.ScanCode) {\r
110 case SCAN_LEFT:\r
111 break;\r
112\r
113 case SCAN_RIGHT:\r
114 break;\r
115\r
116 case SCAN_ESC:\r
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
608 if (!IsHexDigit (&Digital, Key.UnicodeChar)) {\r
609 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
610 break;\r
611 }\r
612 } else {\r
613 if (Key.UnicodeChar > L'9' || Key.UnicodeChar < L'0') {\r
614 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
615 break;\r
616 }\r
617 }\r
618\r
619 //\r
620 // If Count exceed input width, there is no way more is valid\r
621 //\r
622 if (Count >= InputWidth) {\r
623 break;\r
624 }\r
625 //\r
626 // Someone typed something valid!\r
627 //\r
628 if (Count != 0) {\r
629 if (HexInput) {\r
630 EditValue = LShiftU64 (EditValue, 4) + Digital;\r
631 } else {\r
632 EditValue = MultU64x32 (EditValue, 10) + (Key.UnicodeChar - L'0');\r
633 }\r
634 } else {\r
635 if (HexInput) {\r
636 EditValue = Digital;\r
637 } else {\r
638 EditValue = Key.UnicodeChar - L'0';\r
639 }\r
640 }\r
641\r
642 if (EditValue > Maximum) {\r
643 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, TRUE);\r
40a06b0c 644 ASSERT (Count < sizeof (PreviousNumber) / sizeof (PreviousNumber[0]));\r
7936fb6a 645 EditValue = PreviousNumber[Count];\r
646 break;\r
647 } else {\r
648 UpdateStatusBar (INPUT_ERROR, Question->QuestionFlags, FALSE);\r
649 }\r
650\r
651 Count++;\r
bc166db3 652 ASSERT (Count < (sizeof (PreviousNumber) / sizeof (PreviousNumber[0])));\r
7936fb6a 653 PreviousNumber[Count] = EditValue;\r
654\r
655 PrintCharAt (Column, Row, Key.UnicodeChar);\r
656 Column++;\r
657 }\r
658 break;\r
659 }\r
660 } while (TRUE);\r
661\r
662}\r
663\r
664\r
665/**\r
666 Get selection for OneOf and OrderedList (Left/Right will be ignored).\r
667\r
668 @param Selection Pointer to current selection.\r
669 @param MenuOption Pointer to the current input menu.\r
670\r
671 @retval EFI_SUCCESS If Option input is processed successfully\r
672 @retval EFI_DEVICE_ERROR If operation fails\r
673\r
674**/\r
675EFI_STATUS\r
676GetSelectionInputPopUp (\r
677 IN UI_MENU_SELECTION *Selection,\r
678 IN UI_MENU_OPTION *MenuOption\r
679 )\r
680{\r
681 EFI_STATUS Status;\r
682 EFI_INPUT_KEY Key;\r
683 UINTN Index;\r
684 CHAR16 *StringPtr;\r
685 CHAR16 *TempStringPtr;\r
686 UINTN Index2;\r
687 UINTN TopOptionIndex;\r
688 UINTN HighlightOptionIndex;\r
689 UINTN Start;\r
690 UINTN End;\r
691 UINTN Top;\r
692 UINTN Bottom;\r
693 UINTN PopUpMenuLines;\r
694 UINTN MenuLinesInView;\r
695 UINTN PopUpWidth;\r
696 CHAR16 Character;\r
697 INT32 SavedAttribute;\r
698 BOOLEAN ShowDownArrow;\r
699 BOOLEAN ShowUpArrow;\r
700 UINTN DimensionsWidth;\r
701 LIST_ENTRY *Link;\r
702 BOOLEAN OrderedList;\r
703 UINT8 *ValueArray;\r
704 EFI_HII_VALUE HiiValue;\r
705 EFI_HII_VALUE *HiiValueArray;\r
706 UINTN OptionCount;\r
707 QUESTION_OPTION *OneOfOption;\r
708 QUESTION_OPTION *CurrentOption;\r
709 FORM_BROWSER_STATEMENT *Question;\r
710\r
711 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;\r
712\r
713 ValueArray = NULL;\r
714 CurrentOption = NULL;\r
715 ShowDownArrow = FALSE;\r
716 ShowUpArrow = FALSE;\r
717\r
718 StringPtr = AllocateZeroPool ((gOptionBlockWidth + 1) * 2);\r
719 ASSERT (StringPtr);\r
720\r
721 Question = MenuOption->ThisTag;\r
722 if (Question->Operand == EFI_IFR_ORDERED_LIST_OP) {\r
723 ValueArray = Question->BufferValue;\r
724 OrderedList = TRUE;\r
725 } else {\r
726 OrderedList = FALSE;\r
727 }\r
728\r
729 //\r
730 // Calculate Option count\r
731 //\r
732 if (OrderedList) {\r
733 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
734 if (ValueArray[Index] == 0) {\r
735 break;\r
736 }\r
737 }\r
738\r
739 OptionCount = Index;\r
740 } else {\r
741 OptionCount = 0;\r
742 Link = GetFirstNode (&Question->OptionListHead);\r
743 while (!IsNull (&Question->OptionListHead, Link)) {\r
744 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
745\r
746 OptionCount++;\r
747\r
748 Link = GetNextNode (&Question->OptionListHead, Link);\r
749 }\r
750 }\r
751\r
752 //\r
753 // Prepare HiiValue array\r
754 //\r
755 HiiValueArray = AllocateZeroPool (OptionCount * sizeof (EFI_HII_VALUE));\r
756 ASSERT (HiiValueArray != NULL);\r
757 Link = GetFirstNode (&Question->OptionListHead);\r
758 for (Index = 0; Index < OptionCount; Index++) {\r
759 if (OrderedList) {\r
760 HiiValueArray[Index].Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
761 HiiValueArray[Index].Value.u8 = ValueArray[Index];\r
762 } else {\r
763 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
764 CopyMem (&HiiValueArray[Index], &OneOfOption->Value, sizeof (EFI_HII_VALUE));\r
765 Link = GetNextNode (&Question->OptionListHead, Link);\r
766 }\r
767 }\r
768\r
769 //\r
770 // Move Suppressed Option to list tail\r
771 //\r
772 PopUpMenuLines = 0;\r
773 for (Index = 0; Index < OptionCount; Index++) {\r
774 OneOfOption = ValueToOption (Question, &HiiValueArray[OptionCount - Index - 1]);\r
775 if (OneOfOption == NULL) {\r
776 return EFI_NOT_FOUND;\r
777 }\r
778\r
779 RemoveEntryList (&OneOfOption->Link);\r
780\r
781 if ((OneOfOption->SuppressExpression != NULL) &&\r
782 (OneOfOption->SuppressExpression->Result.Value.b)) {\r
783 //\r
784 // This option is suppressed, insert to tail\r
785 //\r
786 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
787 } else {\r
788 //\r
789 // Insert to head\r
790 //\r
791 InsertHeadList (&Question->OptionListHead, &OneOfOption->Link);\r
792\r
793 PopUpMenuLines++;\r
794 }\r
795 }\r
796\r
797 //\r
798 // Get the number of one of options present and its size\r
799 //\r
800 PopUpWidth = 0;\r
801 HighlightOptionIndex = 0;\r
802 Link = GetFirstNode (&Question->OptionListHead);\r
803 for (Index = 0; Index < PopUpMenuLines; Index++) {\r
804 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
805\r
806 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
807 if (StrLen (StringPtr) > PopUpWidth) {\r
808 PopUpWidth = StrLen (StringPtr);\r
809 }\r
f4113e1f 810 FreePool (StringPtr);\r
7936fb6a 811\r
812 if (!OrderedList && CompareHiiValue (&Question->HiiValue, &OneOfOption->Value, NULL) == 0) {\r
813 //\r
814 // Find current selected Option for OneOf\r
815 //\r
816 HighlightOptionIndex = Index;\r
817 }\r
818\r
819 Link = GetNextNode (&Question->OptionListHead, Link);\r
820 }\r
821\r
822 //\r
823 // Perform popup menu initialization.\r
824 //\r
825 PopUpWidth = PopUpWidth + POPUP_PAD_SPACE_COUNT;\r
826\r
827 SavedAttribute = gST->ConOut->Mode->Attribute;\r
828 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
829\r
830 if ((PopUpWidth + POPUP_FRAME_WIDTH) > DimensionsWidth) {\r
831 PopUpWidth = DimensionsWidth - POPUP_FRAME_WIDTH;\r
832 }\r
833\r
834 Start = (DimensionsWidth - PopUpWidth - POPUP_FRAME_WIDTH) / 2 + gScreenDimensions.LeftColumn;\r
835 End = Start + PopUpWidth + POPUP_FRAME_WIDTH;\r
836 Top = gScreenDimensions.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT;\r
837 Bottom = gScreenDimensions.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - 1;\r
838\r
839 MenuLinesInView = Bottom - Top - 1;\r
840 if (MenuLinesInView >= PopUpMenuLines) {\r
841 Top = Top + (MenuLinesInView - PopUpMenuLines) / 2;\r
842 Bottom = Top + PopUpMenuLines + 1;\r
843 } else {\r
844 ShowDownArrow = TRUE;\r
845 }\r
846\r
847 if (HighlightOptionIndex > (MenuLinesInView - 1)) {\r
848 TopOptionIndex = HighlightOptionIndex - MenuLinesInView + 1;\r
849 } else {\r
850 TopOptionIndex = 0;\r
851 }\r
852\r
853 do {\r
854 //\r
855 // Clear that portion of the screen\r
856 //\r
857 ClearLines (Start, End, Top, Bottom, POPUP_TEXT | POPUP_BACKGROUND);\r
858\r
859 //\r
860 // Draw "One of" pop-up menu\r
861 //\r
862 Character = BOXDRAW_DOWN_RIGHT;\r
863 PrintCharAt (Start, Top, Character);\r
864 for (Index = Start; Index + 2 < End; Index++) {\r
865 if ((ShowUpArrow) && ((Index + 1) == (Start + End) / 2)) {\r
866 Character = GEOMETRICSHAPE_UP_TRIANGLE;\r
867 } else {\r
868 Character = BOXDRAW_HORIZONTAL;\r
869 }\r
870\r
871 PrintChar (Character);\r
872 }\r
873\r
874 Character = BOXDRAW_DOWN_LEFT;\r
875 PrintChar (Character);\r
876 Character = BOXDRAW_VERTICAL;\r
877 for (Index = Top + 1; Index < Bottom; Index++) {\r
878 PrintCharAt (Start, Index, Character);\r
879 PrintCharAt (End - 1, Index, Character);\r
880 }\r
881\r
882 //\r
883 // Move to top Option\r
884 //\r
885 Link = GetFirstNode (&Question->OptionListHead);\r
886 for (Index = 0; Index < TopOptionIndex; Index++) {\r
887 Link = GetNextNode (&Question->OptionListHead, Link);\r
888 }\r
889\r
890 //\r
891 // Display the One of options\r
892 //\r
893 Index2 = Top + 1;\r
894 for (Index = TopOptionIndex; (Index < PopUpMenuLines) && (Index2 < Bottom); Index++) {\r
895 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
896 Link = GetNextNode (&Question->OptionListHead, Link);\r
897\r
898 StringPtr = GetToken (OneOfOption->Text, MenuOption->Handle);\r
899 //\r
900 // If the string occupies multiple lines, truncate it to fit in one line,\r
901 // and append a "..." for indication.\r
902 //\r
903 if (StrLen (StringPtr) > (PopUpWidth - 1)) {\r
904 TempStringPtr = AllocateZeroPool (sizeof (CHAR16) * (PopUpWidth - 1));\r
905 ASSERT ( TempStringPtr != NULL );\r
906 CopyMem (TempStringPtr, StringPtr, (sizeof (CHAR16) * (PopUpWidth - 5)));\r
f4113e1f 907 FreePool (StringPtr);\r
7936fb6a 908 StringPtr = TempStringPtr;\r
909 StrCat (StringPtr, L"...");\r
910 }\r
911\r
912 if (Index == HighlightOptionIndex) {\r
913 //\r
914 // Highlight the selected one\r
915 //\r
916 CurrentOption = OneOfOption;\r
917\r
918 gST->ConOut->SetAttribute (gST->ConOut, PICKLIST_HIGHLIGHT_TEXT | PICKLIST_HIGHLIGHT_BACKGROUND);\r
919 PrintStringAt (Start + 2, Index2, StringPtr);\r
920 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
921 } else {\r
922 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);\r
923 PrintStringAt (Start + 2, Index2, StringPtr);\r
924 }\r
925\r
926 Index2++;\r
f4113e1f 927 FreePool (StringPtr);\r
7936fb6a 928 }\r
929\r
930 Character = BOXDRAW_UP_RIGHT;\r
931 PrintCharAt (Start, Bottom, Character);\r
932 for (Index = Start; Index + 2 < End; Index++) {\r
933 if ((ShowDownArrow) && ((Index + 1) == (Start + End) / 2)) {\r
934 Character = GEOMETRICSHAPE_DOWN_TRIANGLE;\r
935 } else {\r
936 Character = BOXDRAW_HORIZONTAL;\r
937 }\r
938\r
939 PrintChar (Character);\r
940 }\r
941\r
942 Character = BOXDRAW_UP_LEFT;\r
943 PrintChar (Character);\r
944\r
945 //\r
946 // Get User selection\r
947 //\r
948 Key.UnicodeChar = CHAR_NULL;\r
949 if ((gDirection == SCAN_UP) || (gDirection == SCAN_DOWN)) {\r
950 Key.ScanCode = gDirection;\r
951 gDirection = 0;\r
952 goto TheKey;\r
953 }\r
954\r
955 Status = WaitForKeyStroke (&Key);\r
956\r
957TheKey:\r
958 switch (Key.UnicodeChar) {\r
959 case '+':\r
960 if (OrderedList) {\r
961 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
962 //\r
963 // Highlight reaches the top of the popup window, scroll one menu item.\r
964 //\r
965 TopOptionIndex--;\r
966 ShowDownArrow = TRUE;\r
967 }\r
968\r
969 if (TopOptionIndex == 0) {\r
970 ShowUpArrow = FALSE;\r
971 }\r
972\r
973 if (HighlightOptionIndex > 0) {\r
974 HighlightOptionIndex--;\r
975\r
40a06b0c 976 ASSERT (CurrentOption != NULL);\r
7936fb6a 977 SwapListEntries (CurrentOption->Link.BackLink, &CurrentOption->Link);\r
978 }\r
979 }\r
980 break;\r
981\r
982 case '-':\r
983 //\r
984 // If an ordered list op-code, we will allow for a popup of +/- keys\r
985 // to create an ordered list of items\r
986 //\r
987 if (OrderedList) {\r
988 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
989 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
990 //\r
991 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
992 //\r
993 TopOptionIndex++;\r
994 ShowUpArrow = TRUE;\r
995 }\r
996\r
997 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
998 ShowDownArrow = FALSE;\r
999 }\r
1000\r
1001 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1002 HighlightOptionIndex++;\r
1003\r
40a06b0c 1004 ASSERT (CurrentOption != NULL);\r
7936fb6a 1005 SwapListEntries (&CurrentOption->Link, CurrentOption->Link.ForwardLink);\r
1006 }\r
1007 }\r
1008 break;\r
1009\r
1010 case CHAR_NULL:\r
1011 switch (Key.ScanCode) {\r
1012 case SCAN_UP:\r
1013 case SCAN_DOWN:\r
1014 if (Key.ScanCode == SCAN_UP) {\r
1015 if ((TopOptionIndex > 0) && (TopOptionIndex == HighlightOptionIndex)) {\r
1016 //\r
1017 // Highlight reaches the top of the popup window, scroll one menu item.\r
1018 //\r
1019 TopOptionIndex--;\r
1020 ShowDownArrow = TRUE;\r
1021 }\r
1022\r
1023 if (TopOptionIndex == 0) {\r
1024 ShowUpArrow = FALSE;\r
1025 }\r
1026\r
1027 if (HighlightOptionIndex > 0) {\r
1028 HighlightOptionIndex--;\r
1029 }\r
1030 } else {\r
1031 if (((TopOptionIndex + MenuLinesInView) < PopUpMenuLines) &&\r
1032 (HighlightOptionIndex == (TopOptionIndex + MenuLinesInView - 1))) {\r
1033 //\r
1034 // Highlight reaches the bottom of the popup window, scroll one menu item.\r
1035 //\r
1036 TopOptionIndex++;\r
1037 ShowUpArrow = TRUE;\r
1038 }\r
1039\r
1040 if ((TopOptionIndex + MenuLinesInView) == PopUpMenuLines) {\r
1041 ShowDownArrow = FALSE;\r
1042 }\r
1043\r
1044 if (HighlightOptionIndex < (PopUpMenuLines - 1)) {\r
1045 HighlightOptionIndex++;\r
1046 }\r
1047 }\r
1048 break;\r
1049\r
1050 case SCAN_ESC:\r
1051 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
1052\r
1053 //\r
1054 // Restore link list order for orderedlist\r
1055 //\r
1056 if (OrderedList) {\r
1057 HiiValue.Type = EFI_IFR_TYPE_NUM_SIZE_8;\r
1058 HiiValue.Value.u64 = 0;\r
1059 for (Index = 0; Index < Question->MaxContainers; Index++) {\r
1060 HiiValue.Value.u8 = ValueArray[Index];\r
d1a54e2c 1061 if (HiiValue.Value.u8 != 0) {\r
7936fb6a 1062 break;\r
1063 }\r
1064\r
1065 OneOfOption = ValueToOption (Question, &HiiValue);\r
1066 if (OneOfOption == NULL) {\r
1067 return EFI_NOT_FOUND;\r
1068 }\r
1069\r
1070 RemoveEntryList (&OneOfOption->Link);\r
1071 InsertTailList (&Question->OptionListHead, &OneOfOption->Link);\r
1072 }\r
1073 }\r
1074\r
f4113e1f 1075 FreePool (HiiValueArray);\r
7936fb6a 1076 return EFI_DEVICE_ERROR;\r
1077\r
1078 default:\r
1079 break;\r
1080 }\r
1081\r
1082 break;\r
1083\r
1084 case CHAR_CARRIAGE_RETURN:\r
1085 //\r
1086 // return the current selection\r
1087 //\r
1088 if (OrderedList) {\r
1089 Index = 0;\r
1090 Link = GetFirstNode (&Question->OptionListHead);\r
1091 while (!IsNull (&Question->OptionListHead, Link)) {\r
1092 OneOfOption = QUESTION_OPTION_FROM_LINK (Link);\r
1093\r
1094 Question->BufferValue[Index] = OneOfOption->Value.Value.u8;\r
1095\r
1096 Index++;\r
1097 if (Index > Question->MaxContainers) {\r
1098 break;\r
1099 }\r
1100\r
1101 Link = GetNextNode (&Question->OptionListHead, Link);\r
1102 }\r
1103 } else {\r
40a06b0c 1104 ASSERT (CurrentOption != NULL);\r
7936fb6a 1105 CopyMem (&Question->HiiValue, &CurrentOption->Value, sizeof (EFI_HII_VALUE));\r
1106 }\r
1107\r
1108 gST->ConOut->SetAttribute (gST->ConOut, SavedAttribute);\r
f4113e1f 1109 FreePool (HiiValueArray);\r
7936fb6a 1110\r
1111 Status = ValidateQuestion (Selection->FormSet, Selection->Form, Question, EFI_HII_EXPRESSION_INCONSISTENT_IF);\r
1112 if (EFI_ERROR (Status)) {\r
1113 //\r
1114 // Input value is not valid, restore Question Value\r
1115 //\r
1116 GetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1117 } else {\r
1118 SetQuestionValue (Selection->FormSet, Selection->Form, Question, TRUE);\r
1119 UpdateStatusBar (NV_UPDATE_REQUIRED, Question->QuestionFlags, TRUE);\r
1120 }\r
1121\r
1122 return Status;\r
1123\r
1124 default:\r
1125 break;\r
1126 }\r
1127 } while (TRUE);\r
1128\r
1129}\r
1130\r
1131/**\r
1132 Wait for a key to be pressed by user.\r
1133\r
1134 @param Key The key which is pressed by user.\r
1135\r
1136 @retval EFI_SUCCESS The function always completed successfully.\r
1137\r
1138**/\r
1139EFI_STATUS\r
1140WaitForKeyStroke (\r
1141 OUT EFI_INPUT_KEY *Key\r
1142 )\r
1143{\r
1144 EFI_STATUS Status;\r
1145\r
1146 do {\r
1147 UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, 0);\r
1148 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, Key);\r
1149 } while (EFI_ERROR(Status));\r
1150\r
1151 return Status;\r
1152}\r