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