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