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