]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
MdeModulePkg: Fixed 'variable set but not used' build warning.
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / Ui.c
1 /** @file
2 Utility functions for User Interface functions.
3
4 Copyright (c) 2004 - 2013, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Setup.h"
16
17 LIST_ENTRY gMenuOption;
18 LIST_ENTRY gMenuList = INITIALIZE_LIST_HEAD_VARIABLE (gMenuList);
19 MENU_REFRESH_ENTRY *gMenuRefreshHead; // Menu list used for refresh timer opcode.
20 MENU_REFRESH_ENTRY *gMenuEventGuidRefreshHead; // Menu list used for refresh event guid opcode.
21
22 //
23 // Search table for UiDisplayMenu()
24 //
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
26 {
27 SCAN_UP,
28 UiUp,
29 },
30 {
31 SCAN_DOWN,
32 UiDown,
33 },
34 {
35 SCAN_PAGE_UP,
36 UiPageUp,
37 },
38 {
39 SCAN_PAGE_DOWN,
40 UiPageDown,
41 },
42 {
43 SCAN_ESC,
44 UiReset,
45 },
46 {
47 SCAN_LEFT,
48 UiLeft,
49 },
50 {
51 SCAN_RIGHT,
52 UiRight,
53 }
54 };
55
56 UINTN mScanCodeNumber = sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]);
57
58 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
59 {
60 UiNoOperation,
61 CfUiNoOperation,
62 },
63 {
64 UiSelect,
65 CfUiSelect,
66 },
67 {
68 UiUp,
69 CfUiUp,
70 },
71 {
72 UiDown,
73 CfUiDown,
74 },
75 {
76 UiLeft,
77 CfUiLeft,
78 },
79 {
80 UiRight,
81 CfUiRight,
82 },
83 {
84 UiReset,
85 CfUiReset,
86 },
87 {
88 UiPageUp,
89 CfUiPageUp,
90 },
91 {
92 UiPageDown,
93 CfUiPageDown
94 },
95 {
96 UiHotKey,
97 CfUiHotKey
98 }
99 };
100
101 BOOLEAN mInputError;
102 BOOLEAN GetLineByWidthFinished = FALSE;
103
104
105 /**
106 Set Buffer to Value for Size bytes.
107
108 @param Buffer Memory to set.
109 @param Size Number of bytes to set
110 @param Value Value of the set operation.
111
112 **/
113 VOID
114 SetUnicodeMem (
115 IN VOID *Buffer,
116 IN UINTN Size,
117 IN CHAR16 Value
118 )
119 {
120 CHAR16 *Ptr;
121
122 Ptr = Buffer;
123 while ((Size--) != 0) {
124 *(Ptr++) = Value;
125 }
126 }
127
128
129 /**
130 Initialize Menu option list.
131
132 **/
133 VOID
134 UiInitMenu (
135 VOID
136 )
137 {
138 InitializeListHead (&gMenuOption);
139 }
140
141
142 /**
143 Free Menu option linked list.
144
145 **/
146 VOID
147 UiFreeMenu (
148 VOID
149 )
150 {
151 UI_MENU_OPTION *MenuOption;
152
153 while (!IsListEmpty (&gMenuOption)) {
154 MenuOption = MENU_OPTION_FROM_LINK (gMenuOption.ForwardLink);
155 RemoveEntryList (&MenuOption->Link);
156
157 //
158 // We allocated space for this description when we did a GetToken, free it here
159 //
160 if (MenuOption->Skip != 0) {
161 //
162 // For date/time, MenuOption->Description is shared by three Menu Options
163 // Data format : [01/02/2004] [11:22:33]
164 // Line number : 0 0 1 0 0 1
165 //
166 FreePool (MenuOption->Description);
167 }
168 FreePool (MenuOption);
169 }
170 }
171
172
173 /**
174 Create a menu with specified formset GUID and form ID, and add it as a child
175 of the given parent menu.
176
177 @param Parent The parent of menu to be added.
178 @param HiiHandle Hii handle related to this formset.
179 @param FormSetGuid The Formset Guid of menu to be added.
180 @param FormId The Form ID of menu to be added.
181
182 @return A pointer to the newly added menu or NULL if memory is insufficient.
183
184 **/
185 UI_MENU_LIST *
186 UiAddMenuList (
187 IN OUT UI_MENU_LIST *Parent,
188 IN EFI_HII_HANDLE HiiHandle,
189 IN EFI_GUID *FormSetGuid,
190 IN UINT16 FormId
191 )
192 {
193 UI_MENU_LIST *MenuList;
194
195 MenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
196 if (MenuList == NULL) {
197 return NULL;
198 }
199
200 MenuList->Signature = UI_MENU_LIST_SIGNATURE;
201 InitializeListHead (&MenuList->ChildListHead);
202
203 MenuList->HiiHandle = HiiHandle;
204 CopyMem (&MenuList->FormSetGuid, FormSetGuid, sizeof (EFI_GUID));
205 MenuList->FormId = FormId;
206 MenuList->Parent = Parent;
207
208 if (Parent == NULL) {
209 //
210 // If parent is not specified, it is the root Form of a Formset
211 //
212 InsertTailList (&gMenuList, &MenuList->Link);
213 } else {
214 InsertTailList (&Parent->ChildListHead, &MenuList->Link);
215 }
216
217 return MenuList;
218 }
219
220
221 /**
222 Search Menu with given FormId and FormSetGuid in all cached menu list.
223
224 @param Parent The parent of menu to search.
225 @param FormSetGuid The Formset GUID of the menu to search.
226 @param FormId The Form ID of menu to search.
227
228 @return A pointer to menu found or NULL if not found.
229
230 **/
231 UI_MENU_LIST *
232 UiFindChildMenuList (
233 IN UI_MENU_LIST *Parent,
234 IN EFI_GUID *FormSetGuid,
235 IN UINT16 FormId
236 )
237 {
238 LIST_ENTRY *Link;
239 UI_MENU_LIST *Child;
240 UI_MENU_LIST *MenuList;
241
242 ASSERT (Parent != NULL);
243
244 if (Parent->FormId == FormId && CompareGuid (FormSetGuid, &Parent->FormSetGuid)) {
245 return Parent;
246 }
247
248 Link = GetFirstNode (&Parent->ChildListHead);
249 while (!IsNull (&Parent->ChildListHead, Link)) {
250 Child = UI_MENU_LIST_FROM_LINK (Link);
251
252 MenuList = UiFindChildMenuList (Child, FormSetGuid, FormId);
253 if (MenuList != NULL) {
254 return MenuList;
255 }
256
257 Link = GetNextNode (&Parent->ChildListHead, Link);
258 }
259
260 return NULL;
261 }
262
263
264 /**
265 Search Menu with given FormSetGuid and FormId in all cached menu list.
266
267 @param FormSetGuid The Formset GUID of the menu to search.
268 @param FormId The Form ID of menu to search.
269
270 @return A pointer to menu found or NULL if not found.
271
272 **/
273 UI_MENU_LIST *
274 UiFindMenuList (
275 IN EFI_GUID *FormSetGuid,
276 IN UINT16 FormId
277 )
278 {
279 LIST_ENTRY *Link;
280 UI_MENU_LIST *MenuList;
281 UI_MENU_LIST *Child;
282
283 Link = GetFirstNode (&gMenuList);
284 while (!IsNull (&gMenuList, Link)) {
285 MenuList = UI_MENU_LIST_FROM_LINK (Link);
286
287 Child = UiFindChildMenuList(MenuList, FormSetGuid, FormId);
288 if (Child != NULL) {
289 return Child;
290 }
291
292 Link = GetNextNode (&gMenuList, Link);
293 }
294
295 return NULL;
296 }
297
298
299 /**
300 Free Menu option linked list.
301
302 **/
303 VOID
304 UiFreeRefreshList (
305 VOID
306 )
307 {
308 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
309
310 while (gMenuRefreshHead != NULL) {
311 OldMenuRefreshEntry = gMenuRefreshHead->Next;
312 FreePool (gMenuRefreshHead);
313 gMenuRefreshHead = OldMenuRefreshEntry;
314 }
315
316 while (gMenuEventGuidRefreshHead != NULL) {
317 OldMenuRefreshEntry = gMenuEventGuidRefreshHead->Next;
318 if (gMenuEventGuidRefreshHead != NULL) {
319 gBS->CloseEvent(gMenuEventGuidRefreshHead->Event);
320 }
321 FreePool (gMenuEventGuidRefreshHead);
322 gMenuEventGuidRefreshHead = OldMenuRefreshEntry;
323 }
324 }
325
326
327 /**
328 Process option string for date/time opcode.
329
330 @param MenuOption Menu option point to date/time.
331 @param OptionString Option string input for process.
332 @param AddOptCol Whether need to update MenuOption->OptCol.
333
334 **/
335 VOID
336 ProcessStringForDateTime (
337 UI_MENU_OPTION *MenuOption,
338 CHAR16 *OptionString,
339 BOOLEAN AddOptCol
340 )
341 {
342 UINTN Index;
343 UINTN Count;
344 FORM_BROWSER_STATEMENT *Statement;
345
346 ASSERT (MenuOption != NULL && OptionString != NULL);
347
348 Statement = MenuOption->ThisTag;
349
350 //
351 // If leading spaces on OptionString - remove the spaces
352 //
353 for (Index = 0; OptionString[Index] == L' '; Index++) {
354 //
355 // Base on the blockspace to get the option column info.
356 //
357 if (AddOptCol) {
358 MenuOption->OptCol++;
359 }
360 }
361
362 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
363 OptionString[Count] = OptionString[Index];
364 Count++;
365 }
366 OptionString[Count] = CHAR_NULL;
367
368 //
369 // Enable to suppress field in the opcode base on the flag.
370 //
371 if (Statement->Operand == EFI_IFR_DATE_OP) {
372 //
373 // OptionString format is: <**: **: ****>
374 // |month|day|year|
375 // 4 3 5
376 //
377 if ((Statement->Flags & EFI_QF_DATE_MONTH_SUPPRESS) && (MenuOption->Sequence == 0)) {
378 //
379 // At this point, only "<**:" in the optionstring.
380 // Clean the day's ** field, after clean, the format is "< :"
381 //
382 SetUnicodeMem (&OptionString[1], 2, L' ');
383 } else if ((Statement->Flags & EFI_QF_DATE_DAY_SUPPRESS) && (MenuOption->Sequence == 1)) {
384 //
385 // At this point, only "**:" in the optionstring.
386 // Clean the month's "**" field, after clean, the format is " :"
387 //
388 SetUnicodeMem (&OptionString[0], 2, L' ');
389 } else if ((Statement->Flags & EFI_QF_DATE_YEAR_SUPPRESS) && (MenuOption->Sequence == 2)) {
390 //
391 // At this point, only "****>" in the optionstring.
392 // Clean the year's "****" field, after clean, the format is " >"
393 //
394 SetUnicodeMem (&OptionString[0], 4, L' ');
395 }
396 } else if (Statement->Operand == EFI_IFR_TIME_OP) {
397 //
398 // OptionString format is: <**: **: **>
399 // |hour|minute|second|
400 // 4 3 3
401 //
402 if ((Statement->Flags & QF_TIME_HOUR_SUPPRESS) && (MenuOption->Sequence == 0)) {
403 //
404 // At this point, only "<**:" in the optionstring.
405 // Clean the hour's ** field, after clean, the format is "< :"
406 //
407 SetUnicodeMem (&OptionString[1], 2, L' ');
408 } else if ((Statement->Flags & QF_TIME_MINUTE_SUPPRESS) && (MenuOption->Sequence == 1)) {
409 //
410 // At this point, only "**:" in the optionstring.
411 // Clean the minute's "**" field, after clean, the format is " :"
412 //
413 SetUnicodeMem (&OptionString[0], 2, L' ');
414 } else if ((Statement->Flags & QF_TIME_SECOND_SUPPRESS) && (MenuOption->Sequence == 2)) {
415 //
416 // At this point, only "**>" in the optionstring.
417 // Clean the second's "**" field, after clean, the format is " >"
418 //
419 SetUnicodeMem (&OptionString[0], 2, L' ');
420 }
421 }
422 }
423
424 /**
425 Refresh question.
426
427 @param MenuRefreshEntry Menu refresh structure which has info about the refresh question.
428 **/
429 EFI_STATUS
430 RefreshQuestion (
431 IN MENU_REFRESH_ENTRY *MenuRefreshEntry
432 )
433 {
434 CHAR16 *OptionString;
435 EFI_STATUS Status;
436 UI_MENU_SELECTION *Selection;
437 FORM_BROWSER_STATEMENT *Question;
438
439 Selection = MenuRefreshEntry->Selection;
440 Question = MenuRefreshEntry->MenuOption->ThisTag;
441
442 Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, GetSetValueWithHiiDriver);
443 if (EFI_ERROR (Status)) {
444 return Status;
445 }
446
447 OptionString = NULL;
448 ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
449
450 if (OptionString != NULL) {
451 //
452 // If old Text is longer than new string, need to clean the old string before paint the newer.
453 // This option is no need for time/date opcode, because time/data opcode has fixed string length.
454 //
455 if ((MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_DATE_OP) &&
456 (MenuRefreshEntry->MenuOption->ThisTag->Operand != EFI_IFR_TIME_OP)) {
457 ClearLines (
458 MenuRefreshEntry->CurrentColumn,
459 MenuRefreshEntry->CurrentColumn + gOptionBlockWidth - 1,
460 MenuRefreshEntry->CurrentRow,
461 MenuRefreshEntry->CurrentRow,
462 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
463 );
464 }
465
466 gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
467 ProcessStringForDateTime(MenuRefreshEntry->MenuOption, OptionString, FALSE);
468 PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
469 FreePool (OptionString);
470 }
471
472 //
473 // Question value may be changed, need invoke its Callback()
474 //
475 Status = ProcessCallBackFunction (Selection, Question, EFI_BROWSER_ACTION_CHANGING, FALSE);
476
477 return Status;
478 }
479
480 /**
481 Refresh the question which has refresh guid event attribute.
482
483 @param Event The event which has this function related.
484 @param Context The input context info related to this event or the status code return to the caller.
485 **/
486 VOID
487 EFIAPI
488 RefreshQuestionNotify(
489 IN EFI_EVENT Event,
490 IN VOID *Context
491 )
492 {
493 MENU_REFRESH_ENTRY *MenuRefreshEntry;
494 UI_MENU_SELECTION *Selection;
495
496 //
497 // Reset FormPackage update flag
498 //
499 mHiiPackageListUpdated = FALSE;
500
501 MenuRefreshEntry = (MENU_REFRESH_ENTRY *)Context;
502 ASSERT (MenuRefreshEntry != NULL);
503 Selection = MenuRefreshEntry->Selection;
504
505 RefreshQuestion (MenuRefreshEntry);
506
507 if (mHiiPackageListUpdated) {
508 //
509 // Package list is updated, force to reparse IFR binary of target Formset
510 //
511 mHiiPackageListUpdated = FALSE;
512 Selection->Action = UI_ACTION_REFRESH_FORMSET;
513 }
514 }
515
516
517 /**
518 Refresh screen.
519
520 **/
521 EFI_STATUS
522 RefreshForm (
523 VOID
524 )
525 {
526 MENU_REFRESH_ENTRY *MenuRefreshEntry;
527 EFI_STATUS Status;
528 UI_MENU_SELECTION *Selection;
529
530 if (gMenuRefreshHead != NULL) {
531 //
532 // call from refresh interval process.
533 //
534 MenuRefreshEntry = gMenuRefreshHead;
535 Selection = MenuRefreshEntry->Selection;
536 //
537 // Reset FormPackage update flag
538 //
539 mHiiPackageListUpdated = FALSE;
540
541 do {
542 Status = RefreshQuestion (MenuRefreshEntry);
543 if (EFI_ERROR (Status)) {
544 return Status;
545 }
546
547 MenuRefreshEntry = MenuRefreshEntry->Next;
548
549 } while (MenuRefreshEntry != NULL);
550
551 if (mHiiPackageListUpdated) {
552 //
553 // Package list is updated, force to reparse IFR binary of target Formset
554 //
555 mHiiPackageListUpdated = FALSE;
556 Selection->Action = UI_ACTION_REFRESH_FORMSET;
557 return EFI_SUCCESS;
558 }
559 }
560
561 return EFI_TIMEOUT;
562 }
563
564
565 /**
566 Wait for a given event to fire, or for an optional timeout to expire.
567
568 @param Event The event to wait for
569 @param Timeout An optional timeout value in 100 ns units.
570 @param RefreshInterval Menu refresh interval (in seconds).
571
572 @retval EFI_SUCCESS Event fired before Timeout expired.
573 @retval EFI_TIME_OUT Timout expired before Event fired.
574
575 **/
576 EFI_STATUS
577 UiWaitForSingleEvent (
578 IN EFI_EVENT Event,
579 IN UINT64 Timeout, OPTIONAL
580 IN UINT8 RefreshInterval OPTIONAL
581 )
582 {
583 EFI_STATUS Status;
584 UINTN Index;
585 EFI_EVENT TimerEvent;
586 EFI_EVENT WaitList[2];
587
588 if (Timeout != 0) {
589 //
590 // Create a timer event
591 //
592 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
593 if (!EFI_ERROR (Status)) {
594 //
595 // Set the timer event
596 //
597 gBS->SetTimer (
598 TimerEvent,
599 TimerRelative,
600 Timeout
601 );
602
603 //
604 // Wait for the original event or the timer
605 //
606 WaitList[0] = Event;
607 WaitList[1] = TimerEvent;
608 Status = gBS->WaitForEvent (2, WaitList, &Index);
609 gBS->CloseEvent (TimerEvent);
610
611 //
612 // If the timer expired, change the return to timed out
613 //
614 if (!EFI_ERROR (Status) && Index == 1) {
615 Status = EFI_TIMEOUT;
616 }
617 }
618 } else {
619 //
620 // Update screen every second
621 //
622 if (RefreshInterval == 0) {
623 Timeout = ONE_SECOND;
624 } else {
625 Timeout = RefreshInterval * ONE_SECOND;
626 }
627
628 do {
629 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
630
631 //
632 // Set the timer event
633 //
634 gBS->SetTimer (
635 TimerEvent,
636 TimerRelative,
637 Timeout
638 );
639
640 //
641 // Wait for the original event or the timer
642 //
643 WaitList[0] = Event;
644 WaitList[1] = TimerEvent;
645 Status = gBS->WaitForEvent (2, WaitList, &Index);
646
647 //
648 // If the timer expired, update anything that needs a refresh and keep waiting
649 //
650 if (!EFI_ERROR (Status) && Index == 1) {
651 Status = EFI_TIMEOUT;
652 if (RefreshInterval != 0) {
653 Status = RefreshForm ();
654 }
655 }
656
657 gBS->CloseEvent (TimerEvent);
658 } while (Status == EFI_TIMEOUT);
659 }
660
661 return Status;
662 }
663
664
665 /**
666 Add one menu option by specified description and context.
667
668 @param String String description for this option.
669 @param Handle Hii handle for the package list.
670 @param Form The form this statement belong to.
671 @param Statement Statement of this Menu Option.
672 @param NumberOfLines Display lines for this Menu Option.
673 @param MenuItemCount The index for this Option in the Menu.
674
675 @retval Pointer Pointer to the added Menu Option.
676
677 **/
678 UI_MENU_OPTION *
679 UiAddMenuOption (
680 IN CHAR16 *String,
681 IN EFI_HII_HANDLE Handle,
682 IN FORM_BROWSER_FORM *Form,
683 IN FORM_BROWSER_STATEMENT *Statement,
684 IN UINT16 NumberOfLines,
685 IN UINT16 MenuItemCount
686 )
687 {
688 UI_MENU_OPTION *MenuOption;
689 UINTN Index;
690 UINTN Count;
691
692 Count = 1;
693 MenuOption = NULL;
694
695 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
696 //
697 // Add three MenuOptions for Date/Time
698 // Data format : [01/02/2004] [11:22:33]
699 // Line number : 0 0 1 0 0 1
700 //
701 NumberOfLines = 0;
702 Count = 3;
703
704 if (Statement->Storage == NULL) {
705 //
706 // For RTC type of date/time, set default refresh interval to be 1 second
707 //
708 if (Statement->RefreshInterval == 0) {
709 Statement->RefreshInterval = 1;
710 }
711 }
712 }
713
714 for (Index = 0; Index < Count; Index++) {
715 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
716 ASSERT (MenuOption);
717
718 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
719 MenuOption->Description = String;
720 MenuOption->Handle = Handle;
721 MenuOption->ThisTag = Statement;
722 MenuOption->EntryNumber = MenuItemCount;
723
724 if (Index == 2) {
725 //
726 // Override LineNumber for the MenuOption in Date/Time sequence
727 //
728 MenuOption->Skip = 1;
729 } else {
730 MenuOption->Skip = NumberOfLines;
731 }
732 MenuOption->Sequence = Index;
733
734 if (EvaluateExpressionList(Statement->Expression, FALSE, NULL, NULL) == ExpressGrayOut ) {
735 MenuOption->GrayOut = TRUE;
736 } else {
737 MenuOption->GrayOut = FALSE;
738 }
739
740 //
741 // If the form or the question has the lock attribute, deal same as grayout.
742 //
743 if (Form->Locked || Statement->Locked) {
744 MenuOption->GrayOut = TRUE;
745 }
746
747 switch (Statement->Operand) {
748 case EFI_IFR_ORDERED_LIST_OP:
749 case EFI_IFR_ONE_OF_OP:
750 case EFI_IFR_NUMERIC_OP:
751 case EFI_IFR_TIME_OP:
752 case EFI_IFR_DATE_OP:
753 case EFI_IFR_CHECKBOX_OP:
754 case EFI_IFR_PASSWORD_OP:
755 case EFI_IFR_STRING_OP:
756 //
757 // User could change the value of these items
758 //
759 MenuOption->IsQuestion = TRUE;
760 break;
761
762 case EFI_IFR_TEXT_OP:
763 if (FeaturePcdGet (PcdBrowserGrayOutTextStatement)) {
764 //
765 // Initializing GrayOut option as TRUE for Text setup options
766 // so that those options will be Gray in colour and un selectable.
767 //
768 MenuOption->GrayOut = TRUE;
769 }
770 //
771 // break skipped on purpose
772 //
773 default:
774 MenuOption->IsQuestion = FALSE;
775 break;
776 }
777
778 if ((Statement->ValueExpression != NULL) ||
779 ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) {
780 MenuOption->ReadOnly = TRUE;
781 if (FeaturePcdGet (PcdBrowerGrayOutReadOnlyMenu)) {
782 MenuOption->GrayOut = TRUE;
783 }
784 }
785
786 InsertTailList (&gMenuOption, &MenuOption->Link);
787 }
788
789 return MenuOption;
790 }
791
792
793 /**
794 Routine used to abstract a generic dialog interface and return the selected key or string
795
796 @param NumberOfLines The number of lines for the dialog box
797 @param HotKey Defines whether a single character is parsed
798 (TRUE) and returned in KeyValue or a string is
799 returned in StringBuffer. Two special characters
800 are considered when entering a string, a SCAN_ESC
801 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
802 string input and returns
803 @param MaximumStringSize The maximum size in bytes of a typed in string
804 (each character is a CHAR16) and the minimum
805 string returned is two bytes
806 @param StringBuffer The passed in pointer to the buffer which will
807 hold the typed in string if HotKey is FALSE
808 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
809 @param ... A series of (quantity == NumberOfLines) text
810 strings which will be used to construct the dialog
811 box
812
813 @retval EFI_SUCCESS Displayed dialog and received user interaction
814 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
815 (StringBuffer == NULL) && (HotKey == FALSE))
816 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
817
818 **/
819 EFI_STATUS
820 EFIAPI
821 CreateDialog (
822 IN UINTN NumberOfLines,
823 IN BOOLEAN HotKey,
824 IN UINTN MaximumStringSize,
825 OUT CHAR16 *StringBuffer,
826 OUT EFI_INPUT_KEY *KeyValue,
827 ...
828 )
829 {
830 VA_LIST Marker;
831 UINTN Count;
832 EFI_INPUT_KEY Key;
833 UINTN LargestString;
834 CHAR16 *TempString;
835 CHAR16 *BufferedString;
836 CHAR16 *StackString;
837 CHAR16 KeyPad[2];
838 UINTN Start;
839 UINTN Top;
840 UINTN Index;
841 EFI_STATUS Status;
842 BOOLEAN SelectionComplete;
843 UINTN InputOffset;
844 UINTN CurrentAttribute;
845 UINTN DimensionsWidth;
846 UINTN DimensionsHeight;
847
848 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
849 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
850
851 SelectionComplete = FALSE;
852 InputOffset = 0;
853 TempString = AllocateZeroPool (MaximumStringSize * 2);
854 BufferedString = AllocateZeroPool (MaximumStringSize * 2);
855 CurrentAttribute = gST->ConOut->Mode->Attribute;
856
857 ASSERT (TempString);
858 ASSERT (BufferedString);
859
860 //
861 // Zero the outgoing buffer
862 //
863 ZeroMem (StringBuffer, MaximumStringSize);
864
865 if (HotKey) {
866 if (KeyValue == NULL) {
867 return EFI_INVALID_PARAMETER;
868 }
869 } else {
870 if (StringBuffer == NULL) {
871 return EFI_INVALID_PARAMETER;
872 }
873 }
874 //
875 // Disable cursor
876 //
877 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
878
879 LargestString = 0;
880
881 VA_START (Marker, KeyValue);
882
883 //
884 // Determine the largest string in the dialog box
885 // Notice we are starting with 1 since String is the first string
886 //
887 for (Count = 0; Count < NumberOfLines; Count++) {
888 StackString = VA_ARG (Marker, CHAR16 *);
889
890 if (StackString[0] == L' ') {
891 InputOffset = Count + 1;
892 }
893
894 if ((GetStringWidth (StackString) / 2) > LargestString) {
895 //
896 // Size of the string visually and subtract the width by one for the null-terminator
897 //
898 LargestString = (GetStringWidth (StackString) / 2);
899 }
900 }
901 VA_END (Marker);
902
903 Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
904 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
905
906 Count = 0;
907
908 //
909 // Display the Popup
910 //
911 VA_START (Marker, KeyValue);
912 CreateSharedPopUp (LargestString, NumberOfLines, Marker);
913 VA_END (Marker);
914
915 //
916 // Take the first key typed and report it back?
917 //
918 if (HotKey) {
919 Status = WaitForKeyStroke (&Key);
920 ASSERT_EFI_ERROR (Status);
921 CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
922
923 } else {
924 do {
925 Status = WaitForKeyStroke (&Key);
926
927 switch (Key.UnicodeChar) {
928 case CHAR_NULL:
929 switch (Key.ScanCode) {
930 case SCAN_ESC:
931 FreePool (TempString);
932 FreePool (BufferedString);
933 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
934 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
935 return EFI_DEVICE_ERROR;
936
937 default:
938 break;
939 }
940
941 break;
942
943 case CHAR_CARRIAGE_RETURN:
944 SelectionComplete = TRUE;
945 FreePool (TempString);
946 FreePool (BufferedString);
947 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
948 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
949 return EFI_SUCCESS;
950 break;
951
952 case CHAR_BACKSPACE:
953 if (StringBuffer[0] != CHAR_NULL) {
954 for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
955 TempString[Index] = StringBuffer[Index];
956 }
957 //
958 // Effectively truncate string by 1 character
959 //
960 TempString[Index - 1] = CHAR_NULL;
961 StrCpy (StringBuffer, TempString);
962 }
963 //
964 // break skipped on purpose
965 //
966
967 default:
968 //
969 // If it is the beginning of the string, don't worry about checking maximum limits
970 //
971 if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
972 StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
973 StrnCpy (TempString, &Key.UnicodeChar, 1);
974 } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
975 KeyPad[0] = Key.UnicodeChar;
976 KeyPad[1] = CHAR_NULL;
977 StrCat (StringBuffer, KeyPad);
978 StrCat (TempString, KeyPad);
979 }
980 //
981 // If the width of the input string is now larger than the screen, we nee to
982 // adjust the index to start printing portions of the string
983 //
984 SetUnicodeMem (BufferedString, LargestString, L' ');
985
986 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
987
988 if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
989 Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
990 } else {
991 Index = 0;
992 }
993
994 for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
995 BufferedString[Count] = StringBuffer[Index];
996 }
997
998 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
999 break;
1000 }
1001 } while (!SelectionComplete);
1002 }
1003
1004 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
1005 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
1006 return EFI_SUCCESS;
1007 }
1008
1009 /**
1010 Draw a pop up windows based on the dimension, number of lines and
1011 strings specified.
1012
1013 @param RequestedWidth The width of the pop-up.
1014 @param NumberOfLines The number of lines.
1015 @param Marker The variable argument list for the list of string to be printed.
1016
1017 **/
1018 VOID
1019 CreateSharedPopUp (
1020 IN UINTN RequestedWidth,
1021 IN UINTN NumberOfLines,
1022 IN VA_LIST Marker
1023 )
1024 {
1025 UINTN Index;
1026 UINTN Count;
1027 CHAR16 Character;
1028 UINTN Start;
1029 UINTN End;
1030 UINTN Top;
1031 UINTN Bottom;
1032 CHAR16 *String;
1033 UINTN DimensionsWidth;
1034 UINTN DimensionsHeight;
1035
1036 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
1037 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
1038
1039 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1040
1041 if ((RequestedWidth + 2) > DimensionsWidth) {
1042 RequestedWidth = DimensionsWidth - 2;
1043 }
1044
1045 //
1046 // Subtract the PopUp width from total Columns, allow for one space extra on
1047 // each end plus a border.
1048 //
1049 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
1050 End = Start + RequestedWidth + 1;
1051
1052 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
1053 Bottom = Top + NumberOfLines + 2;
1054
1055 Character = BOXDRAW_DOWN_RIGHT;
1056 PrintCharAt (Start, Top, Character);
1057 Character = BOXDRAW_HORIZONTAL;
1058 for (Index = Start; Index + 2 < End; Index++) {
1059 PrintChar (Character);
1060 }
1061
1062 Character = BOXDRAW_DOWN_LEFT;
1063 PrintChar (Character);
1064 Character = BOXDRAW_VERTICAL;
1065
1066 Count = 0;
1067 for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
1068 String = VA_ARG (Marker, CHAR16*);
1069
1070 //
1071 // This will clear the background of the line - we never know who might have been
1072 // here before us. This differs from the next clear in that it used the non-reverse
1073 // video for normal printing.
1074 //
1075 if (GetStringWidth (String) / 2 > 1) {
1076 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
1077 }
1078
1079 //
1080 // Passing in a space results in the assumption that this is where typing will occur
1081 //
1082 if (String[0] == L' ') {
1083 ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
1084 }
1085
1086 //
1087 // Passing in a NULL results in a blank space
1088 //
1089 if (String[0] == CHAR_NULL) {
1090 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
1091 }
1092
1093 PrintStringAt (
1094 ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
1095 Index + 1,
1096 String
1097 );
1098 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
1099 PrintCharAt (Start, Index + 1, Character);
1100 PrintCharAt (End - 1, Index + 1, Character);
1101 }
1102
1103 Character = BOXDRAW_UP_RIGHT;
1104 PrintCharAt (Start, Bottom - 1, Character);
1105 Character = BOXDRAW_HORIZONTAL;
1106 for (Index = Start; Index + 2 < End; Index++) {
1107 PrintChar (Character);
1108 }
1109
1110 Character = BOXDRAW_UP_LEFT;
1111 PrintChar (Character);
1112 }
1113
1114 /**
1115 Draw a pop up windows based on the dimension, number of lines and
1116 strings specified.
1117
1118 @param RequestedWidth The width of the pop-up.
1119 @param NumberOfLines The number of lines.
1120 @param ... A series of text strings that displayed in the pop-up.
1121
1122 **/
1123 VOID
1124 EFIAPI
1125 CreateMultiStringPopUp (
1126 IN UINTN RequestedWidth,
1127 IN UINTN NumberOfLines,
1128 ...
1129 )
1130 {
1131 VA_LIST Marker;
1132
1133 VA_START (Marker, NumberOfLines);
1134
1135 CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
1136
1137 VA_END (Marker);
1138 }
1139
1140
1141 /**
1142 Update status bar on the bottom of menu.
1143
1144 @param Selection Current Selction info.
1145 @param MessageType The type of message to be shown.
1146 @param Flags The flags in Question header.
1147 @param State Set or clear.
1148
1149 **/
1150 VOID
1151 UpdateStatusBar (
1152 IN UI_MENU_SELECTION *Selection,
1153 IN UINTN MessageType,
1154 IN UINT8 Flags,
1155 IN BOOLEAN State
1156 )
1157 {
1158 UINTN Index;
1159 CHAR16 *NvUpdateMessage;
1160 CHAR16 *InputErrorMessage;
1161 LIST_ENTRY *Link;
1162 FORM_BROWSER_FORMSET *LocalFormSet;
1163 FORM_BROWSER_STATEMENT *Question;
1164
1165 NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
1166 InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
1167
1168 switch (MessageType) {
1169 case INPUT_ERROR:
1170 if (State) {
1171 gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
1172 PrintStringAt (
1173 gScreenDimensions.LeftColumn + gPromptBlockWidth,
1174 gScreenDimensions.BottomRow - 1,
1175 InputErrorMessage
1176 );
1177 mInputError = TRUE;
1178 } else {
1179 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor));
1180 for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
1181 PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" ");
1182 }
1183
1184 mInputError = FALSE;
1185 }
1186 break;
1187
1188 case NV_UPDATE_REQUIRED:
1189 //
1190 // Global setting support. Show configuration change on every form.
1191 //
1192 if (State) {
1193 gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
1194
1195 if (Selection != NULL && Selection->Statement != NULL) {
1196 Question = Selection->Statement;
1197 if (Question->Storage != NULL || Question->Operand == EFI_IFR_DATE_OP || Question->Operand == EFI_IFR_TIME_OP) {
1198 //
1199 // Update only for Question value that need to be saved into Storage.
1200 //
1201 Selection->Form->NvUpdateRequired = TRUE;
1202 }
1203 }
1204
1205 if (Selection == NULL || IsNvUpdateRequired (Selection->FormSet)) {
1206 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
1207 PrintStringAt (
1208 gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
1209 gScreenDimensions.BottomRow - 1,
1210 NvUpdateMessage
1211 );
1212 }
1213 } else {
1214 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor));
1215 for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
1216 PrintAt (
1217 (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
1218 gScreenDimensions.BottomRow - 1,
1219 L" "
1220 );
1221 }
1222 }
1223 break;
1224
1225 case REFRESH_STATUS_BAR:
1226 if (mInputError) {
1227 UpdateStatusBar (Selection, INPUT_ERROR, Flags, TRUE);
1228 }
1229
1230 switch (gBrowserSettingScope) {
1231 case SystemLevel:
1232 //
1233 // Check the maintain list to see whether there is any change.
1234 //
1235 Link = GetFirstNode (&gBrowserFormSetList);
1236 while (!IsNull (&gBrowserFormSetList, Link)) {
1237 LocalFormSet = FORM_BROWSER_FORMSET_FROM_LINK (Link);
1238 if (IsNvUpdateRequired(LocalFormSet)) {
1239 UpdateStatusBar (NULL, NV_UPDATE_REQUIRED, Flags, TRUE);
1240 break;
1241 }
1242 Link = GetNextNode (&gBrowserFormSetList, Link);
1243 }
1244 break;
1245 case FormSetLevel:
1246 case FormLevel:
1247 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, Flags, TRUE);
1248 default:
1249 break;
1250 }
1251
1252 break;
1253
1254 default:
1255 break;
1256 }
1257
1258 FreePool (InputErrorMessage);
1259 FreePool (NvUpdateMessage);
1260 return ;
1261 }
1262
1263
1264 /**
1265 Get the supported width for a particular op-code
1266
1267 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1268 @param Handle The handle in the HII database being used
1269
1270 @return Returns the number of CHAR16 characters that is support.
1271
1272 **/
1273 UINT16
1274 GetWidth (
1275 IN FORM_BROWSER_STATEMENT *Statement,
1276 IN EFI_HII_HANDLE Handle
1277 )
1278 {
1279 CHAR16 *String;
1280 UINTN Size;
1281 UINT16 Width;
1282
1283 Size = 0;
1284
1285 //
1286 // See if the second text parameter is really NULL
1287 //
1288 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1289 String = GetToken (Statement->TextTwo, Handle);
1290 Size = StrLen (String);
1291 FreePool (String);
1292 }
1293
1294 if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
1295 (Statement->Operand == EFI_IFR_REF_OP) ||
1296 (Statement->Operand == EFI_IFR_PASSWORD_OP) ||
1297 (Statement->Operand == EFI_IFR_ACTION_OP) ||
1298 (Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
1299 //
1300 // Allow a wide display if text op-code and no secondary text op-code
1301 //
1302 ((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
1303 ) {
1304 Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
1305 } else {
1306 Width = (UINT16) gPromptBlockWidth;
1307 }
1308
1309 if (Statement->InSubtitle) {
1310 Width -= SUBTITLE_INDENT;
1311 }
1312
1313 return (UINT16) (Width - LEFT_SKIPPED_COLUMNS);
1314 }
1315
1316 /**
1317 Will copy LineWidth amount of a string in the OutputString buffer and return the
1318 number of CHAR16 characters that were copied into the OutputString buffer.
1319 The output string format is:
1320 Glyph Info + String info + '\0'.
1321
1322 In the code, it deals \r,\n,\r\n same as \n\r, also it not process the \r or \g.
1323
1324 @param InputString String description for this option.
1325 @param LineWidth Width of the desired string to extract in CHAR16
1326 characters
1327 @param GlyphWidth The glyph width of the begin of the char in the string.
1328 @param Index Where in InputString to start the copy process
1329 @param OutputString Buffer to copy the string into
1330
1331 @return Returns the number of CHAR16 characters that were copied into the OutputString
1332 buffer, include extra glyph info and '\0' info.
1333
1334 **/
1335 UINT16
1336 GetLineByWidth (
1337 IN CHAR16 *InputString,
1338 IN UINT16 LineWidth,
1339 IN OUT UINT16 *GlyphWidth,
1340 IN OUT UINTN *Index,
1341 OUT CHAR16 **OutputString
1342 )
1343 {
1344 UINT16 StrOffset;
1345 UINT16 GlyphOffset;
1346 UINT16 OriginalGlyphWidth;
1347 BOOLEAN ReturnFlag;
1348 UINT16 LastSpaceOffset;
1349 UINT16 LastGlyphWidth;
1350
1351 if (InputString == NULL || Index == NULL || OutputString == NULL) {
1352 return 0;
1353 }
1354
1355 if (LineWidth == 0 || *GlyphWidth == 0) {
1356 return 0;
1357 }
1358
1359 //
1360 // Save original glyph width.
1361 //
1362 OriginalGlyphWidth = *GlyphWidth;
1363 LastGlyphWidth = OriginalGlyphWidth;
1364 ReturnFlag = FALSE;
1365 LastSpaceOffset = 0;
1366
1367 //
1368 // NARROW_CHAR can not be printed in screen, so if a line only contain the two CHARs: 'NARROW_CHAR + CHAR_CARRIAGE_RETURN' , it is a empty line in Screen.
1369 // To avoid displaying this empty line in screen, just skip the two CHARs here.
1370 //
1371 if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
1372 *Index = *Index + 2;
1373 }
1374
1375 //
1376 // Fast-forward the string and see if there is a carriage-return in the string
1377 //
1378 for (StrOffset = 0, GlyphOffset = 0; GlyphOffset <= LineWidth; StrOffset++) {
1379 switch (InputString[*Index + StrOffset]) {
1380 case NARROW_CHAR:
1381 *GlyphWidth = 1;
1382 break;
1383
1384 case WIDE_CHAR:
1385 *GlyphWidth = 2;
1386 break;
1387
1388 case CHAR_CARRIAGE_RETURN:
1389 case CHAR_LINEFEED:
1390 case CHAR_NULL:
1391 ReturnFlag = TRUE;
1392 break;
1393
1394 default:
1395 GlyphOffset = GlyphOffset + *GlyphWidth;
1396
1397 //
1398 // Record the last space info in this line. Will be used in rewind.
1399 //
1400 if ((InputString[*Index + StrOffset] == CHAR_SPACE) && (GlyphOffset <= LineWidth)) {
1401 LastSpaceOffset = StrOffset;
1402 LastGlyphWidth = *GlyphWidth;
1403 }
1404 break;
1405 }
1406
1407 if (ReturnFlag) {
1408 break;
1409 }
1410 }
1411
1412 //
1413 // Rewind the string from the maximum size until we see a space to break the line
1414 //
1415 if (GlyphOffset > LineWidth) {
1416 //
1417 // Rewind the string to last space char in this line.
1418 //
1419 if (LastSpaceOffset != 0) {
1420 StrOffset = LastSpaceOffset;
1421 *GlyphWidth = LastGlyphWidth;
1422 } else {
1423 //
1424 // Roll back to last char in the line width.
1425 //
1426 StrOffset--;
1427 }
1428 }
1429
1430 //
1431 // The CHAR_NULL has process last time, this time just return 0 to stand for the end.
1432 //
1433 if (StrOffset == 0 && (InputString[*Index + StrOffset] == CHAR_NULL)) {
1434 return 0;
1435 }
1436
1437 //
1438 // Need extra glyph info and '\0' info, so +2.
1439 //
1440 *OutputString = AllocateZeroPool (((UINTN) (StrOffset + 2) * sizeof(CHAR16)));
1441 if (*OutputString == NULL) {
1442 return 0;
1443 }
1444
1445 //
1446 // Save the glyph info at the begin of the string, will used by Print function.
1447 //
1448 if (OriginalGlyphWidth == 1) {
1449 *(*OutputString) = NARROW_CHAR;
1450 } else {
1451 *(*OutputString) = WIDE_CHAR;
1452 }
1453
1454 CopyMem ((*OutputString) + 1, &InputString[*Index], StrOffset * sizeof(CHAR16));
1455
1456 if (InputString[*Index + StrOffset] == CHAR_SPACE) {
1457 //
1458 // Skip the space info at the begin of next line.
1459 //
1460 *Index = (UINT16) (*Index + StrOffset + 1);
1461 } else if (InputString[*Index + StrOffset] == CHAR_LINEFEED) {
1462 //
1463 // Skip the /n or /n/r info.
1464 //
1465 if (InputString[*Index + StrOffset + 1] == CHAR_CARRIAGE_RETURN) {
1466 *Index = (UINT16) (*Index + StrOffset + 2);
1467 } else {
1468 *Index = (UINT16) (*Index + StrOffset + 1);
1469 }
1470 } else if (InputString[*Index + StrOffset] == CHAR_CARRIAGE_RETURN) {
1471 //
1472 // Skip the /r or /r/n info.
1473 //
1474 if (InputString[*Index + StrOffset + 1] == CHAR_LINEFEED) {
1475 *Index = (UINT16) (*Index + StrOffset + 2);
1476 } else {
1477 *Index = (UINT16) (*Index + StrOffset + 1);
1478 }
1479 } else {
1480 *Index = (UINT16) (*Index + StrOffset);
1481 }
1482
1483 //
1484 // Include extra glyph info and '\0' info, so +2.
1485 //
1486 return StrOffset + 2;
1487 }
1488
1489
1490 /**
1491 Update display lines for a Menu Option.
1492
1493 @param Selection The user's selection.
1494 @param MenuOption The MenuOption to be checked.
1495
1496 **/
1497 VOID
1498 UpdateOptionSkipLines (
1499 IN UI_MENU_SELECTION *Selection,
1500 IN UI_MENU_OPTION *MenuOption
1501 )
1502 {
1503 UINTN Index;
1504 UINT16 Width;
1505 UINTN Row;
1506 UINTN OriginalRow;
1507 CHAR16 *OutputString;
1508 CHAR16 *OptionString;
1509 UINT16 GlyphWidth;
1510
1511 Row = 0;
1512 OptionString = NULL;
1513 Width = (UINT16) gOptionBlockWidth;
1514 OriginalRow = 0;
1515 GlyphWidth = 1;
1516
1517 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1518 if (OptionString == NULL) {
1519 return;
1520 }
1521
1522 for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
1523 //
1524 // If there is more string to process print on the next row and increment the Skip value
1525 //
1526 if (StrLen (&OptionString[Index]) != 0) {
1527 Row++;
1528 //
1529 // Since the Number of lines for this menu entry may or may not be reflected accurately
1530 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1531 // some testing to ensure we are keeping this in-sync.
1532 //
1533 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1534 //
1535 if ((Row - OriginalRow) >= MenuOption->Skip) {
1536 MenuOption->Skip++;
1537 }
1538 }
1539
1540 FreePool (OutputString);
1541 }
1542
1543 if (OptionString != NULL) {
1544 FreePool (OptionString);
1545 }
1546 }
1547
1548
1549 /**
1550 Check whether this Menu Option could be highlighted.
1551
1552 This is an internal function.
1553
1554 @param MenuOption The MenuOption to be checked.
1555
1556 @retval TRUE This Menu Option is selectable.
1557 @retval FALSE This Menu Option could not be selected.
1558
1559 **/
1560 BOOLEAN
1561 IsSelectable (
1562 UI_MENU_OPTION *MenuOption
1563 )
1564 {
1565 if ((MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) ||
1566 MenuOption->GrayOut || MenuOption->ReadOnly) {
1567 return FALSE;
1568 } else {
1569 return TRUE;
1570 }
1571 }
1572
1573
1574 /**
1575 Determine if the menu is the last menu that can be selected.
1576
1577 This is an internal function.
1578
1579 @param Direction The scroll direction. False is down. True is up.
1580 @param CurrentPos The current focus.
1581
1582 @return FALSE -- the menu isn't the last menu that can be selected.
1583 @return TRUE -- the menu is the last menu that can be selected.
1584
1585 **/
1586 BOOLEAN
1587 ValueIsScroll (
1588 IN BOOLEAN Direction,
1589 IN LIST_ENTRY *CurrentPos
1590 )
1591 {
1592 LIST_ENTRY *Temp;
1593
1594 Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
1595
1596 if (Temp == &gMenuOption) {
1597 return TRUE;
1598 }
1599
1600 return FALSE;
1601 }
1602
1603
1604 /**
1605 Move to next selectable statement.
1606
1607 This is an internal function.
1608
1609 @param Selection Menu selection.
1610 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1611 @param CurrentPosition Current position.
1612 @param GapToTop Gap position to top or bottom.
1613
1614 @return The row distance from current MenuOption to next selectable MenuOption.
1615
1616 @retval -1 Reach the begin of the menu, still can't find the selectable menu.
1617 @retval Value Find the selectable menu, maybe the truly selectable, maybe the l
1618 last menu showing at current form.
1619
1620 **/
1621 INTN
1622 MoveToNextStatement (
1623 IN UI_MENU_SELECTION *Selection,
1624 IN BOOLEAN GoUp,
1625 IN OUT LIST_ENTRY **CurrentPosition,
1626 IN UINTN GapToTop
1627 )
1628 {
1629 INTN Distance;
1630 LIST_ENTRY *Pos;
1631 UI_MENU_OPTION *NextMenuOption;
1632 UI_MENU_OPTION *PreMenuOption;
1633
1634 Distance = 0;
1635 Pos = *CurrentPosition;
1636 PreMenuOption = MENU_OPTION_FROM_LINK (Pos);
1637
1638 while (TRUE) {
1639 NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1640 //
1641 // NextMenuOption->Row == 0 means this menu has not calculate
1642 // the NextMenuOption->Skip value yet, just calculate here.
1643 //
1644 if (NextMenuOption->Row == 0) {
1645 UpdateOptionSkipLines (Selection, NextMenuOption);
1646 }
1647
1648 if (GoUp && (PreMenuOption != NextMenuOption)) {
1649 //
1650 // In this case, still can't find the selectable menu,
1651 // return the last one in the showing form.
1652 //
1653 if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
1654 NextMenuOption = PreMenuOption;
1655 break;
1656 }
1657
1658 //
1659 // Current Position doesn't need to be caculated when go up.
1660 // Caculate distanct at first when go up
1661 //
1662 Distance += NextMenuOption->Skip;
1663 }
1664
1665 if (IsSelectable (NextMenuOption)) {
1666 break;
1667 }
1668
1669 //
1670 // Arrive at begin of the menu list.
1671 //
1672 if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &gMenuOption) {
1673 Distance = -1;
1674 break;
1675 }
1676
1677 if (!GoUp) {
1678 //
1679 // In this case, still can't find the selectable menu,
1680 // return the last one in the showing form.
1681 //
1682 if ((UINTN) Distance + NextMenuOption->Skip > GapToTop) {
1683 NextMenuOption = PreMenuOption;
1684 break;
1685 }
1686
1687 Distance += NextMenuOption->Skip;
1688 }
1689
1690 PreMenuOption = NextMenuOption;
1691 Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
1692 }
1693
1694 *CurrentPosition = &NextMenuOption->Link;
1695 return Distance;
1696 }
1697
1698
1699 /**
1700 Adjust Data and Time position accordingly.
1701 Data format : [01/02/2004] [11:22:33]
1702 Line number : 0 0 1 0 0 1
1703
1704 This is an internal function.
1705
1706 @param DirectionUp the up or down direction. False is down. True is
1707 up.
1708 @param CurrentPosition Current position. On return: Point to the last
1709 Option (Year or Second) if up; Point to the first
1710 Option (Month or Hour) if down.
1711
1712 @return Return line number to pad. It is possible that we stand on a zero-advance
1713 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1714
1715 **/
1716 UINTN
1717 AdjustDateAndTimePosition (
1718 IN BOOLEAN DirectionUp,
1719 IN OUT LIST_ENTRY **CurrentPosition
1720 )
1721 {
1722 UINTN Count;
1723 LIST_ENTRY *NewPosition;
1724 UI_MENU_OPTION *MenuOption;
1725 UINTN PadLineNumber;
1726
1727 PadLineNumber = 0;
1728 NewPosition = *CurrentPosition;
1729 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1730
1731 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
1732 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
1733 //
1734 // Calculate the distance from current position to the last Date/Time MenuOption
1735 //
1736 Count = 0;
1737 while (MenuOption->Skip == 0) {
1738 Count++;
1739 NewPosition = NewPosition->ForwardLink;
1740 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1741 PadLineNumber = 1;
1742 }
1743
1744 NewPosition = *CurrentPosition;
1745 if (DirectionUp) {
1746 //
1747 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1748 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1749 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1750 // checking can be done.
1751 //
1752 while (Count++ < 2) {
1753 NewPosition = NewPosition->BackLink;
1754 }
1755 } else {
1756 //
1757 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1758 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1759 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1760 // checking can be done.
1761 //
1762 while (Count-- > 0) {
1763 NewPosition = NewPosition->ForwardLink;
1764 }
1765 }
1766
1767 *CurrentPosition = NewPosition;
1768 }
1769
1770 return PadLineNumber;
1771 }
1772
1773 /**
1774 Find HII Handle in the HII database associated with given Device Path.
1775
1776 If DevicePath is NULL, then ASSERT.
1777
1778 @param DevicePath Device Path associated with the HII package list
1779 handle.
1780
1781 @retval Handle HII package list Handle associated with the Device
1782 Path.
1783 @retval NULL Hii Package list handle is not found.
1784
1785 **/
1786 EFI_HII_HANDLE
1787 EFIAPI
1788 DevicePathToHiiHandle (
1789 IN EFI_DEVICE_PATH_PROTOCOL *DevicePath
1790 )
1791 {
1792 EFI_STATUS Status;
1793 EFI_DEVICE_PATH_PROTOCOL *TmpDevicePath;
1794 UINTN BufferSize;
1795 UINTN HandleCount;
1796 UINTN Index;
1797 EFI_HANDLE Handle;
1798 EFI_HANDLE DriverHandle;
1799 EFI_HII_HANDLE *HiiHandles;
1800 EFI_HII_HANDLE HiiHandle;
1801
1802 ASSERT (DevicePath != NULL);
1803
1804 TmpDevicePath = DevicePath;
1805 //
1806 // Locate Device Path Protocol handle buffer
1807 //
1808 Status = gBS->LocateDevicePath (
1809 &gEfiDevicePathProtocolGuid,
1810 &TmpDevicePath,
1811 &DriverHandle
1812 );
1813 if (EFI_ERROR (Status) || !IsDevicePathEnd (TmpDevicePath)) {
1814 return NULL;
1815 }
1816
1817 //
1818 // Retrieve all HII Handles from HII database
1819 //
1820 BufferSize = 0x1000;
1821 HiiHandles = AllocatePool (BufferSize);
1822 ASSERT (HiiHandles != NULL);
1823 Status = mHiiDatabase->ListPackageLists (
1824 mHiiDatabase,
1825 EFI_HII_PACKAGE_TYPE_ALL,
1826 NULL,
1827 &BufferSize,
1828 HiiHandles
1829 );
1830 if (Status == EFI_BUFFER_TOO_SMALL) {
1831 FreePool (HiiHandles);
1832 HiiHandles = AllocatePool (BufferSize);
1833 ASSERT (HiiHandles != NULL);
1834
1835 Status = mHiiDatabase->ListPackageLists (
1836 mHiiDatabase,
1837 EFI_HII_PACKAGE_TYPE_ALL,
1838 NULL,
1839 &BufferSize,
1840 HiiHandles
1841 );
1842 }
1843
1844 if (EFI_ERROR (Status)) {
1845 FreePool (HiiHandles);
1846 return NULL;
1847 }
1848
1849 //
1850 // Search Hii Handle by Driver Handle
1851 //
1852 HiiHandle = NULL;
1853 HandleCount = BufferSize / sizeof (EFI_HII_HANDLE);
1854 for (Index = 0; Index < HandleCount; Index++) {
1855 Status = mHiiDatabase->GetPackageListHandle (
1856 mHiiDatabase,
1857 HiiHandles[Index],
1858 &Handle
1859 );
1860 if (!EFI_ERROR (Status) && (Handle == DriverHandle)) {
1861 HiiHandle = HiiHandles[Index];
1862 break;
1863 }
1864 }
1865
1866 FreePool (HiiHandles);
1867 return HiiHandle;
1868 }
1869
1870 /**
1871 Find HII Handle in the HII database associated with given form set guid.
1872
1873 If FormSetGuid is NULL, then ASSERT.
1874
1875 @param ComparingGuid FormSet Guid associated with the HII package list
1876 handle.
1877
1878 @retval Handle HII package list Handle associated with the Device
1879 Path.
1880 @retval NULL Hii Package list handle is not found.
1881
1882 **/
1883 EFI_HII_HANDLE
1884 FormSetGuidToHiiHandle (
1885 EFI_GUID *ComparingGuid
1886 )
1887 {
1888 EFI_HII_HANDLE *HiiHandles;
1889 UINTN Index;
1890 EFI_HII_PACKAGE_LIST_HEADER *HiiPackageList;
1891 UINTN BufferSize;
1892 UINT32 Offset;
1893 UINT32 Offset2;
1894 UINT32 PackageListLength;
1895 EFI_HII_PACKAGE_HEADER PackageHeader;
1896 UINT8 *Package;
1897 UINT8 *OpCodeData;
1898 EFI_STATUS Status;
1899 EFI_HII_HANDLE HiiHandle;
1900
1901 ASSERT (ComparingGuid != NULL);
1902
1903 HiiHandle = NULL;
1904 //
1905 // Get all the Hii handles
1906 //
1907 HiiHandles = HiiGetHiiHandles (NULL);
1908 ASSERT (HiiHandles != NULL);
1909
1910 //
1911 // Search for formset of each class type
1912 //
1913 for (Index = 0; HiiHandles[Index] != NULL; Index++) {
1914 BufferSize = 0;
1915 HiiPackageList = NULL;
1916 Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList);
1917 if (Status == EFI_BUFFER_TOO_SMALL) {
1918 HiiPackageList = AllocatePool (BufferSize);
1919 ASSERT (HiiPackageList != NULL);
1920
1921 Status = mHiiDatabase->ExportPackageLists (mHiiDatabase, HiiHandles[Index], &BufferSize, HiiPackageList);
1922 }
1923 if (EFI_ERROR (Status) || HiiPackageList == NULL) {
1924 return NULL;
1925 }
1926
1927 //
1928 // Get Form package from this HII package List
1929 //
1930 Offset = sizeof (EFI_HII_PACKAGE_LIST_HEADER);
1931 Offset2 = 0;
1932 CopyMem (&PackageListLength, &HiiPackageList->PackageLength, sizeof (UINT32));
1933
1934 while (Offset < PackageListLength) {
1935 Package = ((UINT8 *) HiiPackageList) + Offset;
1936 CopyMem (&PackageHeader, Package, sizeof (EFI_HII_PACKAGE_HEADER));
1937
1938 if (PackageHeader.Type == EFI_HII_PACKAGE_FORMS) {
1939 //
1940 // Search FormSet in this Form Package
1941 //
1942 Offset2 = sizeof (EFI_HII_PACKAGE_HEADER);
1943 while (Offset2 < PackageHeader.Length) {
1944 OpCodeData = Package + Offset2;
1945
1946 if (((EFI_IFR_OP_HEADER *) OpCodeData)->OpCode == EFI_IFR_FORM_SET_OP) {
1947 //
1948 // Try to compare against formset GUID
1949 //
1950 if (CompareGuid (ComparingGuid, (EFI_GUID *)(OpCodeData + sizeof (EFI_IFR_OP_HEADER)))) {
1951 HiiHandle = HiiHandles[Index];
1952 break;
1953 }
1954 }
1955
1956 Offset2 += ((EFI_IFR_OP_HEADER *) OpCodeData)->Length;
1957 }
1958 }
1959 if (HiiHandle != NULL) {
1960 break;
1961 }
1962 Offset += PackageHeader.Length;
1963 }
1964
1965 FreePool (HiiPackageList);
1966 if (HiiHandle != NULL) {
1967 break;
1968 }
1969 }
1970
1971 FreePool (HiiHandles);
1972
1973 return HiiHandle;
1974 }
1975
1976 /**
1977 Process the goto op code, update the info in the selection structure.
1978
1979 @param Statement The statement belong to goto op code.
1980 @param Selection The selection info.
1981 @param Repaint Whether need to repaint the menu.
1982 @param NewLine Whether need to create new line.
1983
1984 @retval EFI_SUCCESS The menu process successfully.
1985 @return Other value if the process failed.
1986 **/
1987 EFI_STATUS
1988 ProcessGotoOpCode (
1989 IN OUT FORM_BROWSER_STATEMENT *Statement,
1990 IN OUT UI_MENU_SELECTION *Selection,
1991 OUT BOOLEAN *Repaint,
1992 OUT BOOLEAN *NewLine
1993 )
1994 {
1995 CHAR16 *StringPtr;
1996 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1997 FORM_BROWSER_FORM *RefForm;
1998 EFI_INPUT_KEY Key;
1999 EFI_STATUS Status;
2000 UI_MENU_LIST *MenuList;
2001 BOOLEAN UpdateFormInfo;
2002
2003 Status = EFI_SUCCESS;
2004 UpdateFormInfo = TRUE;
2005 StringPtr = NULL;
2006
2007 //
2008 // Prepare the device path check, get the device path info first.
2009 //
2010 if (Statement->HiiValue.Value.ref.DevicePath != 0) {
2011 StringPtr = GetToken (Statement->HiiValue.Value.ref.DevicePath, Selection->FormSet->HiiHandle);
2012 }
2013
2014 //
2015 // Check whether the device path string is a valid string.
2016 //
2017 if (Statement->HiiValue.Value.ref.DevicePath != 0 && StringPtr != NULL) {
2018 if (Selection->Form->ModalForm) {
2019 return Status;
2020 }
2021
2022 //
2023 // Goto another Hii Package list
2024 //
2025 if (mPathFromText != NULL) {
2026 DevicePath = mPathFromText->ConvertTextToDevicePath(StringPtr);
2027 if (DevicePath != NULL) {
2028 Selection->Handle = DevicePathToHiiHandle (DevicePath);
2029 FreePool (DevicePath);
2030 }
2031 FreePool (StringPtr);
2032 } else {
2033 //
2034 // Not found the EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL protocol.
2035 //
2036 do {
2037 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gProtocolNotFound, gPressEnter, gEmptyString);
2038 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2039 if (Repaint != NULL) {
2040 *Repaint = TRUE;
2041 }
2042 FreePool (StringPtr);
2043 return Status;
2044 }
2045
2046 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2047 if (Selection->Handle == NULL) {
2048 //
2049 // If target Hii Handle not found, exit
2050 //
2051 Selection->Action = UI_ACTION_EXIT;
2052 Selection->Statement = NULL;
2053 return Status;
2054 }
2055
2056 CopyMem (&Selection->FormSetGuid,&Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
2057 Selection->FormId = Statement->HiiValue.Value.ref.FormId;
2058 Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
2059 } else if (!CompareGuid (&Statement->HiiValue.Value.ref.FormSetGuid, &gZeroGuid)) {
2060 if (Selection->Form->ModalForm) {
2061 return Status;
2062 }
2063 //
2064 // Goto another Formset, check for uncommitted data
2065 //
2066 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2067
2068 Selection->Handle = FormSetGuidToHiiHandle(&Statement->HiiValue.Value.ref.FormSetGuid);
2069 if (Selection->Handle == NULL) {
2070 //
2071 // If target Hii Handle not found, exit
2072 //
2073 Selection->Action = UI_ACTION_EXIT;
2074 Selection->Statement = NULL;
2075 return Status;
2076 }
2077
2078 CopyMem (&Selection->FormSetGuid, &Statement->HiiValue.Value.ref.FormSetGuid, sizeof (EFI_GUID));
2079 Selection->FormId = Statement->HiiValue.Value.ref.FormId;
2080 Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
2081 } else if (Statement->HiiValue.Value.ref.FormId != 0) {
2082 //
2083 // Check whether target From is suppressed.
2084 //
2085 RefForm = IdToForm (Selection->FormSet, Statement->HiiValue.Value.ref.FormId);
2086
2087 if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) {
2088 if (EvaluateExpressionList(RefForm->SuppressExpression, TRUE, Selection->FormSet, RefForm) != ExpressFalse) {
2089 //
2090 // Form is suppressed.
2091 //
2092 do {
2093 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString);
2094 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2095 if (Repaint != NULL) {
2096 *Repaint = TRUE;
2097 }
2098 return Status;
2099 }
2100 }
2101
2102 //
2103 // Goto another form inside this formset,
2104 //
2105 Selection->Action = UI_ACTION_REFRESH_FORM;
2106
2107 Selection->FormId = Statement->HiiValue.Value.ref.FormId;
2108 Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
2109 } else if (Statement->HiiValue.Value.ref.QuestionId != 0) {
2110 //
2111 // Goto another Question
2112 //
2113 Selection->QuestionId = Statement->HiiValue.Value.ref.QuestionId;
2114
2115 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2116 Selection->Action = UI_ACTION_REFRESH_FORM;
2117 } else {
2118 if (Repaint != NULL) {
2119 *Repaint = TRUE;
2120 }
2121 if (NewLine != NULL) {
2122 *NewLine = TRUE;
2123 }
2124 }
2125 UpdateFormInfo = FALSE;
2126 } else {
2127 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2128 Selection->Action = UI_ACTION_REFRESH_FORM;
2129 }
2130 UpdateFormInfo = FALSE;
2131 }
2132
2133 if (UpdateFormInfo) {
2134 //
2135 // Link current form so that we can always go back when someone hits the ESC
2136 //
2137 MenuList = UiFindMenuList (&Selection->FormSetGuid, Selection->FormId);
2138 if (MenuList == NULL && Selection->CurrentMenu != NULL) {
2139 MenuList = UiAddMenuList (Selection->CurrentMenu, Selection->Handle, &Selection->FormSetGuid, Selection->FormId);
2140 }
2141 }
2142
2143 return Status;
2144 }
2145
2146 /**
2147 Display menu and wait for user to select one menu option, then return it.
2148 If AutoBoot is enabled, then if user doesn't select any option,
2149 after period of time, it will automatically return the first menu option.
2150
2151 @param Selection Menu selection.
2152
2153 @retval EFI_SUCESSS This function always return successfully for now.
2154
2155 **/
2156 EFI_STATUS
2157 UiDisplayMenu (
2158 IN OUT UI_MENU_SELECTION *Selection
2159 )
2160 {
2161 INTN SkipValue;
2162 INTN Difference;
2163 UINTN DistanceValue;
2164 UINTN Row;
2165 UINTN Col;
2166 UINTN Temp;
2167 UINTN Temp2;
2168 UINTN TopRow;
2169 UINTN BottomRow;
2170 UINTN OriginalRow;
2171 UINTN Index;
2172 UINT16 Width;
2173 CHAR16 *StringPtr;
2174 CHAR16 *OptionString;
2175 CHAR16 *OutputString;
2176 CHAR16 *HelpString;
2177 CHAR16 *HelpHeaderString;
2178 CHAR16 *HelpBottomString;
2179 BOOLEAN NewLine;
2180 BOOLEAN Repaint;
2181 BOOLEAN SavedValue;
2182 BOOLEAN UpArrow;
2183 BOOLEAN DownArrow;
2184 BOOLEAN InitializedFlag;
2185 EFI_STATUS Status;
2186 EFI_INPUT_KEY Key;
2187 LIST_ENTRY *Link;
2188 LIST_ENTRY *NewPos;
2189 LIST_ENTRY *TopOfScreen;
2190 LIST_ENTRY *SavedListEntry;
2191 UI_MENU_OPTION *MenuOption;
2192 UI_MENU_OPTION *NextMenuOption;
2193 UI_MENU_OPTION *SavedMenuOption;
2194 UI_MENU_OPTION *PreviousMenuOption;
2195 UI_CONTROL_FLAG ControlFlag;
2196 EFI_SCREEN_DESCRIPTOR LocalScreen;
2197 MENU_REFRESH_ENTRY *MenuRefreshEntry;
2198 MENU_REFRESH_ENTRY *MenuUpdateEntry;
2199 UI_SCREEN_OPERATION ScreenOperation;
2200 UINT8 MinRefreshInterval;
2201 UINT16 DefaultId;
2202 FORM_BROWSER_STATEMENT *Statement;
2203 UI_MENU_LIST *CurrentMenu;
2204 UINTN ModalSkipColumn;
2205 BROWSER_HOT_KEY *HotKey;
2206 UINTN HelpPageIndex;
2207 UINTN HelpPageCount;
2208 UINTN RowCount;
2209 UINTN HelpLine;
2210 UINTN HelpHeaderLine;
2211 UINTN HelpBottomLine;
2212 BOOLEAN MultiHelpPage;
2213 UINT16 GlyphWidth;
2214 UINT16 EachLineWidth;
2215 UINT16 HeaderLineWidth;
2216 UINT16 BottomLineWidth;
2217
2218 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
2219
2220 Status = EFI_SUCCESS;
2221 HelpString = NULL;
2222 HelpHeaderString = NULL;
2223 HelpBottomString = NULL;
2224 OptionString = NULL;
2225 ScreenOperation = UiNoOperation;
2226 NewLine = TRUE;
2227 MinRefreshInterval = 0;
2228 DefaultId = 0;
2229 HelpPageCount = 0;
2230 HelpLine = 0;
2231 RowCount = 0;
2232 HelpBottomLine = 0;
2233 HelpHeaderLine = 0;
2234 HelpPageIndex = 0;
2235 MultiHelpPage = FALSE;
2236 EachLineWidth = 0;
2237 HeaderLineWidth = 0;
2238 BottomLineWidth = 0;
2239 OutputString = NULL;
2240 UpArrow = FALSE;
2241 DownArrow = FALSE;
2242 SkipValue = 0;
2243 MenuRefreshEntry = gMenuRefreshHead;
2244
2245 NextMenuOption = NULL;
2246 PreviousMenuOption = NULL;
2247 SavedMenuOption = NULL;
2248 HotKey = NULL;
2249 ModalSkipColumn = (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 6;
2250
2251 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
2252
2253 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE){
2254 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
2255 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
2256 } else {
2257 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
2258 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
2259 }
2260
2261 if (Selection->Form->ModalForm) {
2262 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS + ModalSkipColumn;
2263 } else {
2264 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
2265 }
2266
2267 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - gFooterHeight - SCROLL_ARROW_HEIGHT - 1;
2268
2269 Selection->TopRow = TopRow;
2270 Selection->BottomRow = BottomRow;
2271 Selection->PromptCol = Col;
2272 Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
2273 Selection->Statement = NULL;
2274
2275 TopOfScreen = gMenuOption.ForwardLink;
2276 Repaint = TRUE;
2277 MenuOption = NULL;
2278
2279 //
2280 // Find current Menu
2281 //
2282 CurrentMenu = UiFindMenuList (&Selection->FormSetGuid, Selection->FormId);
2283 if (CurrentMenu == NULL) {
2284 //
2285 // Current menu not found, add it to the menu tree
2286 //
2287 CurrentMenu = UiAddMenuList (NULL, Selection->Handle, &Selection->FormSetGuid, Selection->FormId);
2288 }
2289 ASSERT (CurrentMenu != NULL);
2290 Selection->CurrentMenu = CurrentMenu;
2291
2292 if (Selection->QuestionId == 0) {
2293 //
2294 // Highlight not specified, fetch it from cached menu
2295 //
2296 Selection->QuestionId = CurrentMenu->QuestionId;
2297 Selection->Sequence = CurrentMenu->Sequence;
2298 }
2299
2300 //
2301 // Init option as the current user's selection
2302 //
2303 InitializedFlag = TRUE;
2304 NewPos = gMenuOption.ForwardLink;
2305
2306 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
2307 UpdateStatusBar (Selection, REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
2308
2309 ControlFlag = CfInitialization;
2310 Selection->Action = UI_ACTION_NONE;
2311 while (TRUE) {
2312 switch (ControlFlag) {
2313 case CfInitialization:
2314 if (IsListEmpty (&gMenuOption)) {
2315 ControlFlag = CfReadKey;
2316 } else {
2317 ControlFlag = CfCheckSelection;
2318 }
2319 break;
2320
2321 case CfCheckSelection:
2322 if (Selection->Action != UI_ACTION_NONE) {
2323 ControlFlag = CfExit;
2324 } else {
2325 ControlFlag = CfRepaint;
2326 }
2327 break;
2328
2329 case CfRepaint:
2330 ControlFlag = CfRefreshHighLight;
2331
2332 if (Repaint) {
2333 //
2334 // Display menu
2335 //
2336 DownArrow = FALSE;
2337 UpArrow = FALSE;
2338 Row = TopRow;
2339
2340 Temp = (UINTN) SkipValue;
2341 Temp2 = (UINTN) SkipValue;
2342
2343 //
2344 // 1. Clear the screen.
2345 //
2346 if (Selection->Form->ModalForm) {
2347 ClearLines (
2348 LocalScreen.LeftColumn + ModalSkipColumn,
2349 LocalScreen.LeftColumn + ModalSkipColumn + gPromptBlockWidth + gOptionBlockWidth,
2350 TopRow - SCROLL_ARROW_HEIGHT,
2351 BottomRow + SCROLL_ARROW_HEIGHT,
2352 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
2353 );
2354 } else {
2355 ClearLines (
2356 LocalScreen.LeftColumn,
2357 LocalScreen.RightColumn,
2358 TopRow - SCROLL_ARROW_HEIGHT,
2359 BottomRow + SCROLL_ARROW_HEIGHT,
2360 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
2361 );
2362 }
2363 UiFreeRefreshList ();
2364 MinRefreshInterval = 0;
2365
2366 //
2367 // 2.Paint the menu.
2368 //
2369 for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
2370 MenuOption = MENU_OPTION_FROM_LINK (Link);
2371 MenuOption->Row = Row;
2372 MenuOption->Col = Col;
2373 if (Selection->Form->ModalForm) {
2374 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn + ModalSkipColumn;
2375 } else {
2376 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
2377 }
2378
2379 Statement = MenuOption->ThisTag;
2380 if (Statement->InSubtitle) {
2381 MenuOption->Col += SUBTITLE_INDENT;
2382 }
2383
2384 if (MenuOption->GrayOut) {
2385 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
2386 } else {
2387 if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
2388 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
2389 }
2390 }
2391
2392 Width = GetWidth (Statement, MenuOption->Handle);
2393 OriginalRow = Row;
2394 GlyphWidth = 1;
2395
2396 if (Statement->Operand == EFI_IFR_REF_OP && MenuOption->Col >= 2) {
2397 //
2398 // Print Arrow for Goto button.
2399 //
2400 PrintAt (
2401 MenuOption->Col - 2,
2402 Row,
2403 L"%c",
2404 GEOMETRICSHAPE_RIGHT_TRIANGLE
2405 );
2406 }
2407
2408 //
2409 // 2.1. Paint the description.
2410 //
2411 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2412 //
2413 // Temp means need to skip how many lines from the start.
2414 //
2415 if ((Temp == 0) && (Row <= BottomRow)) {
2416 PrintStringAt (MenuOption->Col, Row, OutputString);
2417 }
2418 //
2419 // If there is more string to process print on the next row and increment the Skip value
2420 //
2421 if (StrLen (&MenuOption->Description[Index]) != 0) {
2422 if (Temp == 0) {
2423 Row++;
2424 }
2425 }
2426
2427 FreePool (OutputString);
2428 if (Temp != 0) {
2429 Temp--;
2430 }
2431 }
2432
2433 Temp = 0;
2434 Row = OriginalRow;
2435
2436 //
2437 // 2.2. Paint the option string.
2438 //
2439 Status = ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2440 if (EFI_ERROR (Status)) {
2441 //
2442 // Repaint to clear possible error prompt pop-up
2443 //
2444 Repaint = TRUE;
2445 NewLine = TRUE;
2446 ControlFlag = CfRepaint;
2447 break;
2448 }
2449
2450 if (OptionString != NULL) {
2451 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
2452 ProcessStringForDateTime(MenuOption, OptionString, TRUE);
2453 }
2454
2455 Width = (UINT16) gOptionBlockWidth;
2456 OriginalRow = Row;
2457 GlyphWidth = 1;
2458
2459 for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2460 if ((Temp2 == 0) && (Row <= BottomRow)) {
2461 PrintStringAt (MenuOption->OptCol, Row, OutputString);
2462 }
2463 //
2464 // If there is more string to process print on the next row and increment the Skip value
2465 //
2466 if (StrLen (&OptionString[Index]) != 0) {
2467 if (Temp2 == 0) {
2468 Row++;
2469 //
2470 // Since the Number of lines for this menu entry may or may not be reflected accurately
2471 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2472 // some testing to ensure we are keeping this in-sync.
2473 //
2474 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2475 //
2476 if ((Row - OriginalRow) >= MenuOption->Skip) {
2477 MenuOption->Skip++;
2478 }
2479 }
2480 }
2481
2482 FreePool (OutputString);
2483 if (Temp2 != 0) {
2484 Temp2--;
2485 }
2486 }
2487
2488 Temp2 = 0;
2489 Row = OriginalRow;
2490
2491 FreePool (OptionString);
2492 }
2493
2494 //
2495 // 2.4 Special process for Test opcode with test two.
2496 //
2497 if (!CompareGuid (&Statement->RefreshGuid, &gZeroGuid)) {
2498 if (gMenuEventGuidRefreshHead == NULL) {
2499 MenuUpdateEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
2500 gMenuEventGuidRefreshHead = MenuUpdateEntry;
2501 } else {
2502 MenuUpdateEntry = gMenuEventGuidRefreshHead;
2503 while (MenuUpdateEntry->Next != NULL) {
2504 MenuUpdateEntry = MenuUpdateEntry->Next;
2505 }
2506 MenuUpdateEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
2507 MenuUpdateEntry = MenuUpdateEntry->Next;
2508 }
2509 ASSERT (MenuUpdateEntry != NULL);
2510 Status = gBS->CreateEventEx (EVT_NOTIFY_SIGNAL, TPL_NOTIFY, RefreshQuestionNotify, MenuUpdateEntry, &Statement->RefreshGuid, &MenuUpdateEntry->Event);
2511 ASSERT (!EFI_ERROR (Status));
2512 MenuUpdateEntry->MenuOption = MenuOption;
2513 MenuUpdateEntry->Selection = Selection;
2514 MenuUpdateEntry->CurrentColumn = MenuOption->OptCol;
2515 MenuUpdateEntry->CurrentRow = MenuOption->Row;
2516 if (MenuOption->GrayOut) {
2517 MenuUpdateEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
2518 } else {
2519 MenuUpdateEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
2520 }
2521 }
2522
2523 //
2524 // If Question request refresh, register the op-code
2525 //
2526 if (Statement->RefreshInterval != 0) {
2527 //
2528 // Menu will be refreshed at minimal interval of all Questions
2529 // which have refresh request
2530 //
2531 if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
2532 MinRefreshInterval = Statement->RefreshInterval;
2533 }
2534
2535 if (gMenuRefreshHead == NULL) {
2536 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
2537 gMenuRefreshHead = MenuRefreshEntry;
2538 } else {
2539 MenuRefreshEntry = gMenuRefreshHead;
2540 while (MenuRefreshEntry->Next != NULL) {
2541 MenuRefreshEntry = MenuRefreshEntry->Next;
2542 }
2543 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
2544 MenuRefreshEntry = MenuRefreshEntry->Next;
2545 }
2546 ASSERT (MenuRefreshEntry != NULL);
2547 MenuRefreshEntry->MenuOption = MenuOption;
2548 MenuRefreshEntry->Selection = Selection;
2549 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
2550 MenuRefreshEntry->CurrentRow = MenuOption->Row;
2551 if (MenuOption->GrayOut) {
2552 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
2553 } else {
2554 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
2555 }
2556 }
2557
2558 //
2559 // If this is a text op with secondary text information
2560 //
2561 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
2562 StringPtr = GetToken (Statement->TextTwo, MenuOption->Handle);
2563
2564 Width = (UINT16) gOptionBlockWidth;
2565 OriginalRow = Row;
2566 GlyphWidth = 1;
2567
2568 for (Index = 0; GetLineByWidth (StringPtr, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2569 if ((Temp == 0) && (Row <= BottomRow)) {
2570 PrintStringAt (MenuOption->OptCol, Row, OutputString);
2571 }
2572 //
2573 // If there is more string to process print on the next row and increment the Skip value
2574 //
2575 if (StrLen (&StringPtr[Index]) != 0) {
2576 if (Temp2 == 0) {
2577 Row++;
2578 //
2579 // Since the Number of lines for this menu entry may or may not be reflected accurately
2580 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
2581 // some testing to ensure we are keeping this in-sync.
2582 //
2583 // If the difference in rows is greater than or equal to the skip value, increase the skip value
2584 //
2585 if ((Row - OriginalRow) >= MenuOption->Skip) {
2586 MenuOption->Skip++;
2587 }
2588 }
2589 }
2590
2591 FreePool (OutputString);
2592 if (Temp2 != 0) {
2593 Temp2--;
2594 }
2595 }
2596
2597 Row = OriginalRow;
2598 FreePool (StringPtr);
2599 }
2600 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2601
2602 //
2603 // 3. Update the row info which will be used by next menu.
2604 //
2605 if (Link == TopOfScreen) {
2606 Row += MenuOption->Skip - SkipValue;
2607 } else {
2608 Row += MenuOption->Skip;
2609 }
2610
2611 if (Row > BottomRow) {
2612 if (!ValueIsScroll (FALSE, Link)) {
2613 DownArrow = TRUE;
2614 }
2615
2616 Row = BottomRow + 1;
2617 break;
2618 }
2619 }
2620
2621 if (!ValueIsScroll (TRUE, TopOfScreen)) {
2622 UpArrow = TRUE;
2623 }
2624
2625 if (UpArrow) {
2626 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2627 PrintAt (
2628 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2629 TopRow - SCROLL_ARROW_HEIGHT,
2630 L"%c",
2631 ARROW_UP
2632 );
2633 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2634 }
2635
2636 if (DownArrow) {
2637 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2638 PrintAt (
2639 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2640 BottomRow + SCROLL_ARROW_HEIGHT,
2641 L"%c",
2642 ARROW_DOWN
2643 );
2644 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2645 }
2646
2647 MenuOption = NULL;
2648 }
2649 break;
2650
2651 case CfRefreshHighLight:
2652 //
2653 // MenuOption: Last menu option that need to remove hilight
2654 // MenuOption is set to NULL in Repaint
2655 // NewPos: Current menu option that need to hilight
2656 //
2657 ControlFlag = CfUpdateHelpString;
2658 if (TopOfScreen == &MenuOption->Link) {
2659 Temp = SkipValue;
2660 } else {
2661 Temp = 0;
2662 }
2663 if (NewPos == TopOfScreen) {
2664 Temp2 = SkipValue;
2665 } else {
2666 Temp2 = 0;
2667 }
2668 if (InitializedFlag) {
2669 InitializedFlag = FALSE;
2670 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
2671 }
2672
2673 //
2674 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2675 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2676 //
2677 SavedValue = Repaint;
2678 Repaint = FALSE;
2679
2680 if (Selection->QuestionId != 0) {
2681 NewPos = gMenuOption.ForwardLink;
2682 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2683
2684 while ((SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId ||
2685 SavedMenuOption->Sequence != Selection->Sequence) &&
2686 NewPos->ForwardLink != &gMenuOption) {
2687 NewPos = NewPos->ForwardLink;
2688 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2689 }
2690 if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
2691 //
2692 // Target Question found, find its MenuOption
2693 //
2694 Link = TopOfScreen;
2695
2696 for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
2697 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2698 Index += SavedMenuOption->Skip;
2699 if (Link == TopOfScreen) {
2700 Index -= SkipValue;
2701 }
2702 Link = Link->ForwardLink;
2703 }
2704 if (NewPos == Link) {
2705 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2706 }
2707
2708 //
2709 // Not find the selected menu in current show page.
2710 // Have two case to enter this if:
2711 // 1. Not find the menu at current page.
2712 // 2. Find the menu in current page, but the menu shows at the bottom and not all info shows.
2713 // For case 2, has an exception: The menu can show more than one pages and now only this menu shows.
2714 //
2715 // Base on the selected menu will show at the bottom of the page,
2716 // select the menu which will show at the top of the page.
2717 //
2718 if (Link != NewPos || Index > BottomRow ||
2719 (Link == NewPos && (SavedMenuOption->Row + SavedMenuOption->Skip - 1 > BottomRow) && (Link != TopOfScreen))) {
2720 //
2721 // Find the MenuOption which has the skip value for Date/Time opcode.
2722 //
2723 AdjustDateAndTimePosition(FALSE, &NewPos);
2724 //
2725 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2726 //
2727 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2728 //
2729 // SavedMenuOption->Row == 0 means the menu not show yet.
2730 //
2731 if (SavedMenuOption->Row == 0) {
2732 UpdateOptionSkipLines (Selection, SavedMenuOption);
2733 }
2734
2735 //
2736 // Base on the selected menu will show at the bottome of next page,
2737 // select the menu show at the top of the next page.
2738 //
2739 Link = NewPos;
2740 for (Index = TopRow + SavedMenuOption->Skip; Index <= BottomRow + 1; ) {
2741 Link = Link->BackLink;
2742 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2743 if (SavedMenuOption->Row == 0) {
2744 UpdateOptionSkipLines (Selection, SavedMenuOption);
2745 }
2746 Index += SavedMenuOption->Skip;
2747 }
2748
2749 //
2750 // Found the menu which will show at the top of the page.
2751 //
2752 if (Link == NewPos) {
2753 //
2754 // The menu can show more than one pages, just show the menu at the top of the page.
2755 //
2756 SkipValue = 0;
2757 TopOfScreen = Link;
2758 } else {
2759 //
2760 // Check whether need to skip some line for menu shows at the top of the page.
2761 //
2762 SkipValue = Index - BottomRow - 1;
2763 if (SkipValue > 0 && SkipValue < (INTN) SavedMenuOption->Skip) {
2764 TopOfScreen = Link;
2765 } else {
2766 SkipValue = 0;
2767 TopOfScreen = Link->ForwardLink;
2768 }
2769 }
2770
2771 Repaint = TRUE;
2772 NewLine = TRUE;
2773 ControlFlag = CfRepaint;
2774 break;
2775 }
2776 } else {
2777 //
2778 // Target Question not found, highlight the default menu option
2779 //
2780 NewPos = TopOfScreen;
2781 }
2782
2783 Selection->QuestionId = 0;
2784 }
2785
2786 if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
2787 if (MenuOption != NULL) {
2788 //
2789 // Remove highlight on last Menu Option
2790 //
2791 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2792 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2793 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2794 if (OptionString != NULL) {
2795 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2796 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
2797 ) {
2798 ProcessStringForDateTime(MenuOption, OptionString, FALSE);
2799 }
2800
2801 Width = (UINT16) gOptionBlockWidth;
2802 OriginalRow = MenuOption->Row;
2803 GlyphWidth = 1;
2804
2805 for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2806 if ((Temp == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow)) {
2807 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2808 }
2809 //
2810 // If there is more string to process print on the next row and increment the Skip value
2811 //
2812 if (StrLen (&OptionString[Index]) != 0) {
2813 if (Temp == 0) {
2814 MenuOption->Row++;
2815 }
2816 }
2817
2818 FreePool (OutputString);
2819 if (Temp != 0) {
2820 Temp--;
2821 }
2822 }
2823
2824 MenuOption->Row = OriginalRow;
2825
2826 FreePool (OptionString);
2827 } else {
2828 if (NewLine) {
2829 if (MenuOption->GrayOut) {
2830 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
2831 } else if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
2832 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
2833 }
2834
2835 OriginalRow = MenuOption->Row;
2836 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
2837 GlyphWidth = 1;
2838
2839 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2840 if ((Temp == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow)) {
2841 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2842 }
2843 //
2844 // If there is more string to process print on the next row and increment the Skip value
2845 //
2846 if (StrLen (&MenuOption->Description[Index]) != 0) {
2847 if (Temp == 0) {
2848 MenuOption->Row++;
2849 }
2850 }
2851
2852 FreePool (OutputString);
2853 if (Temp != 0) {
2854 Temp--;
2855 }
2856 }
2857
2858 MenuOption->Row = OriginalRow;
2859 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2860 }
2861 }
2862 }
2863
2864 //
2865 // This is the current selected statement
2866 //
2867 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2868 Statement = MenuOption->ThisTag;
2869 Selection->Statement = Statement;
2870 if (!IsSelectable (MenuOption)) {
2871 Repaint = SavedValue;
2872 UpdateKeyHelp (Selection, MenuOption, FALSE);
2873 break;
2874 }
2875
2876 //
2877 // Record highlight for current menu
2878 //
2879 CurrentMenu->QuestionId = Statement->QuestionId;
2880 CurrentMenu->Sequence = MenuOption->Sequence;
2881
2882 //
2883 // Set reverse attribute
2884 //
2885 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));
2886 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2887
2888 //
2889 // Assuming that we have a refresh linked-list created, lets annotate the
2890 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2891 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2892 //
2893 if (gMenuRefreshHead != NULL) {
2894 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
2895 if (MenuRefreshEntry->MenuOption->GrayOut) {
2896 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
2897 } else {
2898 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
2899 }
2900 if (MenuRefreshEntry->MenuOption == MenuOption) {
2901 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor);
2902 }
2903 }
2904 }
2905
2906 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2907 if (OptionString != NULL) {
2908 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
2909 ProcessStringForDateTime(MenuOption, OptionString, FALSE);
2910 }
2911 Width = (UINT16) gOptionBlockWidth;
2912
2913 OriginalRow = MenuOption->Row;
2914 GlyphWidth = 1;
2915
2916 for (Index = 0; GetLineByWidth (OptionString, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2917 if ((Temp2 == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow) ) {
2918 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2919 }
2920 //
2921 // If there is more string to process print on the next row and increment the Skip value
2922 //
2923 if (StrLen (&OptionString[Index]) != 0) {
2924 if (Temp2 == 0) {
2925 MenuOption->Row++;
2926 }
2927 }
2928
2929 FreePool (OutputString);
2930 if (Temp2 != 0) {
2931 Temp2--;
2932 }
2933 }
2934
2935 MenuOption->Row = OriginalRow;
2936
2937 FreePool (OptionString);
2938 } else {
2939 if (NewLine) {
2940 OriginalRow = MenuOption->Row;
2941
2942 Width = GetWidth (Statement, MenuOption->Handle);
2943 GlyphWidth = 1;
2944
2945 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &GlyphWidth, &Index, &OutputString) != 0x0000;) {
2946 if ((Temp2 == 0) && (MenuOption->Row >= TopRow) && (MenuOption->Row <= BottomRow) ) {
2947 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2948 }
2949 //
2950 // If there is more string to process print on the next row and increment the Skip value
2951 //
2952 if (StrLen (&MenuOption->Description[Index]) != 0) {
2953 if (Temp2 == 0) {
2954 MenuOption->Row++;
2955 }
2956 }
2957
2958 FreePool (OutputString);
2959 if (Temp2 != 0) {
2960 Temp2--;
2961 }
2962 }
2963
2964 MenuOption->Row = OriginalRow;
2965
2966 }
2967 }
2968
2969 UpdateKeyHelp (Selection, MenuOption, FALSE);
2970
2971 //
2972 // Clear reverse attribute
2973 //
2974 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2975 }
2976 //
2977 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2978 // if we didn't break halfway when process CfRefreshHighLight.
2979 //
2980 Repaint = SavedValue;
2981 break;
2982
2983 case CfUpdateHelpString:
2984 ControlFlag = CfPrepareToReadKey;
2985 if (Selection->Form->ModalForm) {
2986 break;
2987 }
2988
2989 if (Repaint || NewLine) {
2990 //
2991 // Don't print anything if it is a NULL help token
2992 //
2993 ASSERT(MenuOption != NULL);
2994 if (MenuOption->ThisTag->Help == 0 || !IsSelectable (MenuOption)) {
2995 StringPtr = L"\0";
2996 } else {
2997 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2998 }
2999
3000 RowCount = BottomRow - TopRow;
3001 HelpPageIndex = 0;
3002 //
3003 // 1.Calculate how many line the help string need to print.
3004 //
3005 if (HelpString != NULL) {
3006 FreePool (HelpString);
3007 }
3008 HelpLine = ProcessHelpString (StringPtr, &HelpString, &EachLineWidth, RowCount);
3009 if (HelpLine > RowCount) {
3010 MultiHelpPage = TRUE;
3011 StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_UP), gHiiHandle);
3012 if (HelpHeaderString != NULL) {
3013 FreePool (HelpHeaderString);
3014 }
3015 HelpHeaderLine = ProcessHelpString (StringPtr, &HelpHeaderString, &HeaderLineWidth, RowCount);
3016 StringPtr = GetToken (STRING_TOKEN(ADJUST_HELP_PAGE_DOWN), gHiiHandle);
3017 if (HelpBottomString != NULL) {
3018 FreePool (HelpBottomString);
3019 }
3020 HelpBottomLine = ProcessHelpString (StringPtr, &HelpBottomString, &BottomLineWidth, RowCount);
3021 //
3022 // Calculate the help page count.
3023 //
3024 if (HelpLine > 2 * RowCount - 2) {
3025 HelpPageCount = (HelpLine - RowCount + 1) / (RowCount - 2) + 1;
3026 if ((HelpLine - RowCount + 1) % (RowCount - 2) > 1) {
3027 HelpPageCount += 1;
3028 }
3029 } else {
3030 HelpPageCount = 2;
3031 }
3032 } else {
3033 MultiHelpPage = FALSE;
3034 }
3035 }
3036
3037 //
3038 // Clean the help field first.
3039 //
3040 ClearLines (
3041 LocalScreen.RightColumn - gHelpBlockWidth,
3042 LocalScreen.RightColumn,
3043 TopRow,
3044 BottomRow,
3045 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
3046 );
3047
3048 //
3049 // Check whether need to show the 'More(U/u)' at the begin.
3050 // Base on current direct info, here shows aligned to the right side of the column.
3051 // If the direction is multi line and aligned to right side may have problem, so
3052 // add ASSERT code here.
3053 //
3054 if (HelpPageIndex > 0) {
3055 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT | FIELD_BACKGROUND);
3056 for (Index = 0; Index < HelpHeaderLine; Index++) {
3057 ASSERT (HelpHeaderLine == 1);
3058 ASSERT (GetStringWidth (HelpHeaderString) / 2 < (UINTN) (gHelpBlockWidth - 1));
3059 PrintStringAt (
3060 LocalScreen.RightColumn - GetStringWidth (HelpHeaderString) / 2 - 1,
3061 Index + TopRow,
3062 &HelpHeaderString[Index * HeaderLineWidth]
3063 );
3064 }
3065 }
3066
3067 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
3068 //
3069 // Print the help string info.
3070 //
3071 if (!MultiHelpPage) {
3072 for (Index = 0; Index < HelpLine; Index++) {
3073 PrintStringAt (
3074 LocalScreen.RightColumn - gHelpBlockWidth,
3075 Index + TopRow,
3076 &HelpString[Index * EachLineWidth]
3077 );
3078 }
3079 gST->ConOut->SetCursorPosition(gST->ConOut, LocalScreen.RightColumn-1, BottomRow);
3080 } else {
3081 if (HelpPageIndex == 0) {
3082 for (Index = 0; Index < RowCount - HelpBottomLine; Index++) {
3083 PrintStringAt (
3084 LocalScreen.RightColumn - gHelpBlockWidth,
3085 Index + TopRow,
3086 &HelpString[Index * EachLineWidth]
3087 );
3088 }
3089 } else {
3090 for (Index = 0; (Index < RowCount - HelpBottomLine - HelpHeaderLine) &&
3091 (Index + HelpPageIndex * (RowCount - 2) + 1 < HelpLine); Index++) {
3092 PrintStringAt (
3093 LocalScreen.RightColumn - gHelpBlockWidth,
3094 Index + TopRow + HelpHeaderLine,
3095 &HelpString[(Index + HelpPageIndex * (RowCount - 2) + 1)* EachLineWidth]
3096 );
3097 }
3098 if (HelpPageIndex == HelpPageCount - 1) {
3099 gST->ConOut->SetCursorPosition(gST->ConOut, LocalScreen.RightColumn-1, BottomRow);
3100 }
3101 }
3102 }
3103
3104 //
3105 // Check whether need to print the 'More(D/d)' at the bottom.
3106 // Base on current direct info, here shows aligned to the right side of the column.
3107 // If the direction is multi line and aligned to right side may have problem, so
3108 // add ASSERT code here.
3109 //
3110 if (HelpPageIndex < HelpPageCount - 1 && MultiHelpPage) {
3111 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT | FIELD_BACKGROUND);
3112 for (Index = 0; Index < HelpBottomLine; Index++) {
3113 ASSERT (HelpBottomLine == 1);
3114 ASSERT (GetStringWidth (HelpBottomString) / 2 < (UINTN) (gHelpBlockWidth - 1));
3115 PrintStringAt (
3116 LocalScreen.RightColumn - GetStringWidth (HelpBottomString) / 2 - 1,
3117 Index + BottomRow - HelpBottomLine,
3118 &HelpBottomString[Index * BottomLineWidth]
3119 );
3120 }
3121 }
3122 //
3123 // Reset this flag every time we finish using it.
3124 //
3125 Repaint = FALSE;
3126 NewLine = FALSE;
3127 break;
3128
3129 case CfPrepareToReadKey:
3130 ControlFlag = CfReadKey;
3131 ScreenOperation = UiNoOperation;
3132 break;
3133
3134 case CfReadKey:
3135 ControlFlag = CfScreenOperation;
3136
3137 //
3138 // Wait for user's selection
3139 //
3140 while (TRUE) {
3141 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
3142 if (!EFI_ERROR (Status)) {
3143 break;
3144 }
3145
3146 //
3147 // If we encounter error, continue to read another key in.
3148 //
3149 if (Status != EFI_NOT_READY) {
3150 continue;
3151 }
3152
3153 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
3154 ASSERT_EFI_ERROR (Status);
3155
3156 if (Selection->Action == UI_ACTION_REFRESH_FORMSET) {
3157 //
3158 // IFR is updated in Callback of refresh opcode, re-parse it
3159 //
3160 ControlFlag = CfCheckSelection;
3161 Selection->Statement = NULL;
3162 break;
3163 }
3164 }
3165
3166 if (ControlFlag == CfCheckSelection) {
3167 break;
3168 }
3169
3170 switch (Key.UnicodeChar) {
3171 case CHAR_CARRIAGE_RETURN:
3172 if(MenuOption->GrayOut || MenuOption->ReadOnly) {
3173 ControlFlag = CfReadKey;
3174 break;
3175 }
3176
3177 ScreenOperation = UiSelect;
3178 gDirection = 0;
3179 break;
3180
3181 //
3182 // We will push the adjustment of these numeric values directly to the input handler
3183 // NOTE: we won't handle manual input numeric
3184 //
3185 case '+':
3186 case '-':
3187 //
3188 // If the screen has no menu items, and the user didn't select UiReset
3189 // ignore the selection and go back to reading keys.
3190 //
3191 if(IsListEmpty (&gMenuOption) || MenuOption->GrayOut || MenuOption->ReadOnly) {
3192 ControlFlag = CfReadKey;
3193 break;
3194 }
3195
3196 ASSERT(MenuOption != NULL);
3197 Statement = MenuOption->ThisTag;
3198 if ((Statement->Operand == EFI_IFR_DATE_OP)
3199 || (Statement->Operand == EFI_IFR_TIME_OP)
3200 || ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
3201 ){
3202 if (Key.UnicodeChar == '+') {
3203 gDirection = SCAN_RIGHT;
3204 } else {
3205 gDirection = SCAN_LEFT;
3206 }
3207 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
3208 if (EFI_ERROR (Status)) {
3209 //
3210 // Repaint to clear possible error prompt pop-up
3211 //
3212 Repaint = TRUE;
3213 NewLine = TRUE;
3214 } else {
3215 Selection->Action = UI_ACTION_REFRESH_FORM;
3216 }
3217 if (OptionString != NULL) {
3218 FreePool (OptionString);
3219 }
3220 }
3221 break;
3222
3223 case '^':
3224 ScreenOperation = UiUp;
3225 break;
3226
3227 case 'V':
3228 case 'v':
3229 ScreenOperation = UiDown;
3230 break;
3231
3232 case ' ':
3233 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
3234 //
3235 // If the screen has no menu items, and the user didn't select UiReset
3236 // ignore the selection and go back to reading keys.
3237 //
3238 if(IsListEmpty (&gMenuOption)) {
3239 ControlFlag = CfReadKey;
3240 break;
3241 }
3242
3243 ASSERT(MenuOption != NULL);
3244 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut && !MenuOption->ReadOnly) {
3245 ScreenOperation = UiSelect;
3246 }
3247 }
3248 break;
3249
3250 case 'D':
3251 case 'd':
3252 if (!MultiHelpPage) {
3253 ControlFlag = CfReadKey;
3254 break;
3255 }
3256 ControlFlag = CfUpdateHelpString;
3257 HelpPageIndex = HelpPageIndex < HelpPageCount - 1 ? HelpPageIndex + 1 : HelpPageCount - 1;
3258 break;
3259
3260 case 'U':
3261 case 'u':
3262 if (!MultiHelpPage) {
3263 ControlFlag = CfReadKey;
3264 break;
3265 }
3266 ControlFlag = CfUpdateHelpString;
3267 HelpPageIndex = HelpPageIndex > 0 ? HelpPageIndex - 1 : 0;
3268 break;
3269
3270 case CHAR_NULL:
3271 for (Index = 0; Index < mScanCodeNumber; Index++) {
3272 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
3273 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
3274 break;
3275 }
3276 }
3277
3278 if (Selection->Form->ModalForm && (Key.ScanCode == SCAN_ESC || Index == mScanCodeNumber)) {
3279 //
3280 // ModalForm has no ESC key and Hot Key.
3281 //
3282 ControlFlag = CfReadKey;
3283 } else if (Index == mScanCodeNumber) {
3284 //
3285 // Check whether Key matches the registered hot key.
3286 //
3287 HotKey = NULL;
3288 if ((gBrowserSettingScope == SystemLevel) ||
3289 (Selection->FormEditable && gFunctionKeySetting != NONE_FUNCTION_KEY_SETTING)) {
3290 HotKey = GetHotKeyFromRegisterList (&Key);
3291 }
3292 if (HotKey != NULL) {
3293 ScreenOperation = UiHotKey;
3294 }
3295 }
3296 break;
3297 }
3298 break;
3299
3300 case CfScreenOperation:
3301 if (ScreenOperation != UiReset) {
3302 //
3303 // If the screen has no menu items, and the user didn't select UiReset
3304 // ignore the selection and go back to reading keys.
3305 //
3306 if (IsListEmpty (&gMenuOption)) {
3307 ControlFlag = CfReadKey;
3308 break;
3309 }
3310 }
3311
3312 for (Index = 0;
3313 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
3314 Index++
3315 ) {
3316 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
3317 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
3318 break;
3319 }
3320 }
3321 break;
3322
3323 case CfUiSelect:
3324 ControlFlag = CfCheckSelection;
3325
3326 ASSERT(MenuOption != NULL);
3327 Statement = MenuOption->ThisTag;
3328 if (Statement->Operand == EFI_IFR_TEXT_OP) {
3329 break;
3330 }
3331
3332 //
3333 // Keep highlight on current MenuOption
3334 //
3335 Selection->QuestionId = Statement->QuestionId;
3336
3337 switch (Statement->Operand) {
3338 case EFI_IFR_REF_OP:
3339 ProcessGotoOpCode(Statement, Selection, &Repaint, &NewLine);
3340 break;
3341
3342 case EFI_IFR_ACTION_OP:
3343 //
3344 // Process the Config string <ConfigResp>
3345 //
3346 Status = ProcessQuestionConfig (Selection, Statement);
3347
3348 if (EFI_ERROR (Status)) {
3349 break;
3350 }
3351
3352 //
3353 // The action button may change some Question value, so refresh the form
3354 //
3355 Selection->Action = UI_ACTION_REFRESH_FORM;
3356 break;
3357
3358 case EFI_IFR_RESET_BUTTON_OP:
3359 //
3360 // Reset Question to default value specified by DefaultId
3361 //
3362 ControlFlag = CfUiDefault;
3363 DefaultId = Statement->DefaultId;
3364 break;
3365
3366 default:
3367 //
3368 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
3369 //
3370 UpdateKeyHelp (Selection, MenuOption, TRUE);
3371 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
3372
3373 if (EFI_ERROR (Status)) {
3374 Repaint = TRUE;
3375 NewLine = TRUE;
3376 UpdateKeyHelp (Selection, MenuOption, FALSE);
3377 } else {
3378 Selection->Action = UI_ACTION_REFRESH_FORM;
3379 }
3380
3381 if (OptionString != NULL) {
3382 FreePool (OptionString);
3383 }
3384 break;
3385 }
3386 break;
3387
3388 case CfUiReset:
3389 //
3390 // We come here when someone press ESC
3391 //
3392 ControlFlag = CfCheckSelection;
3393 FindNextMenu (Selection, &Repaint, &NewLine);
3394 break;
3395
3396 case CfUiLeft:
3397 ControlFlag = CfCheckSelection;
3398 ASSERT(MenuOption != NULL);
3399 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
3400 if (MenuOption->Sequence != 0) {
3401 //
3402 // In the middle or tail of the Date/Time op-code set, go left.
3403 //
3404 ASSERT(NewPos != NULL);
3405 NewPos = NewPos->BackLink;
3406 }
3407 }
3408 break;
3409
3410 case CfUiRight:
3411 ControlFlag = CfCheckSelection;
3412 ASSERT(MenuOption != NULL);
3413 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
3414 if (MenuOption->Sequence != 2) {
3415 //
3416 // In the middle or tail of the Date/Time op-code set, go left.
3417 //
3418 ASSERT(NewPos != NULL);
3419 NewPos = NewPos->ForwardLink;
3420 }
3421 }
3422 break;
3423
3424 case CfUiUp:
3425 ControlFlag = CfCheckSelection;
3426
3427 SavedListEntry = NewPos;
3428
3429 ASSERT(NewPos != NULL);
3430 //
3431 // Adjust Date/Time position before we advance forward.
3432 //
3433 AdjustDateAndTimePosition (TRUE, &NewPos);
3434 if (NewPos->BackLink != &gMenuOption) {
3435 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3436 ASSERT (MenuOption != NULL);
3437 NewLine = TRUE;
3438 NewPos = NewPos->BackLink;
3439
3440 PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3441 if (PreviousMenuOption->Row == 0) {
3442 UpdateOptionSkipLines (Selection, PreviousMenuOption);
3443 }
3444 DistanceValue = PreviousMenuOption->Skip;
3445 Difference = 0;
3446 if (MenuOption->Row >= DistanceValue + TopRow) {
3447 Difference = MoveToNextStatement (Selection, TRUE, &NewPos, MenuOption->Row - TopRow - DistanceValue);
3448 }
3449 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3450
3451 if (Difference < 0) {
3452 //
3453 // We hit the begining MenuOption that can be focused
3454 // so we simply scroll to the top.
3455 //
3456 if (TopOfScreen != gMenuOption.ForwardLink) {
3457 TopOfScreen = gMenuOption.ForwardLink;
3458 Repaint = TRUE;
3459 } else {
3460 //
3461 // Scroll up to the last page when we have arrived at top page.
3462 //
3463 NewPos = &gMenuOption;
3464 TopOfScreen = &gMenuOption;
3465 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3466 ScreenOperation = UiPageUp;
3467 ControlFlag = CfScreenOperation;
3468 break;
3469 }
3470 } else if (MenuOption->Row < TopRow + DistanceValue + Difference) {
3471 //
3472 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
3473 //
3474 TopOfScreen = NewPos;
3475 Repaint = TRUE;
3476 SkipValue = 0;
3477 } else if (!IsSelectable (NextMenuOption)) {
3478 //
3479 // Continue to go up until scroll to next page or the selectable option is found.
3480 //
3481 ScreenOperation = UiUp;
3482 ControlFlag = CfScreenOperation;
3483 }
3484
3485 //
3486 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3487 //
3488 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3489 AdjustDateAndTimePosition (TRUE, &NewPos);
3490 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3491 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3492 } else {
3493 //
3494 // Scroll up to the last page.
3495 //
3496 NewPos = &gMenuOption;
3497 TopOfScreen = &gMenuOption;
3498 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3499 ScreenOperation = UiPageUp;
3500 ControlFlag = CfScreenOperation;
3501 }
3502 break;
3503
3504 case CfUiPageUp:
3505 //
3506 // SkipValue means lines is skipped when show the top menu option.
3507 //
3508 ControlFlag = CfCheckSelection;
3509
3510 ASSERT(NewPos != NULL);
3511 //
3512 // Already at the first menu option, so do nothing.
3513 //
3514 if (NewPos->BackLink == &gMenuOption) {
3515 NewLine = FALSE;
3516 Repaint = FALSE;
3517 break;
3518 }
3519
3520 NewLine = TRUE;
3521 Repaint = TRUE;
3522
3523 //
3524 // SkipValue > (BottomRow - TopRow + 1) means current menu has more than one
3525 // form of options to be show, so just update the SkipValue to show the next
3526 // parts of options.
3527 //
3528 if (SkipValue > (INTN) (BottomRow - TopRow + 1)) {
3529 SkipValue -= BottomRow - TopRow + 1;
3530 break;
3531 }
3532
3533 Link = TopOfScreen;
3534 //
3535 // First minus the menu of the top screen, it's value is SkipValue.
3536 //
3537 Index = (BottomRow + 1) - SkipValue;
3538 while ((Index >= TopRow) && (Link->BackLink != &gMenuOption)) {
3539 Link = Link->BackLink;
3540 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
3541 if (PreviousMenuOption->Row == 0) {
3542 UpdateOptionSkipLines (Selection, PreviousMenuOption);
3543 }
3544 if (Index < PreviousMenuOption->Skip) {
3545 break;
3546 }
3547 Index = Index - PreviousMenuOption->Skip;
3548 }
3549
3550 if ((Link->BackLink == &gMenuOption) && (Index >= TopRow)) {
3551 SkipValue = 0;
3552 if (TopOfScreen == &gMenuOption) {
3553 TopOfScreen = gMenuOption.ForwardLink;
3554 NewPos = gMenuOption.BackLink;
3555 MoveToNextStatement (Selection, TRUE, &NewPos, BottomRow - TopRow);
3556 Repaint = FALSE;
3557 } else if (TopOfScreen != Link) {
3558 TopOfScreen = Link;
3559 NewPos = Link;
3560 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
3561 } else {
3562 //
3563 // Finally we know that NewPos is the last MenuOption can be focused.
3564 //
3565 Repaint = FALSE;
3566 NewPos = Link;
3567 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
3568 }
3569 } else {
3570 if (Index >= TopRow) {
3571 //
3572 // At here, only case "Index < PreviousMenuOption->Skip" can reach here.
3573 //
3574 SkipValue = PreviousMenuOption->Skip - (Index - TopRow);
3575 } else {
3576 SkipValue = PreviousMenuOption->Skip - (TopRow - Index);
3577 Link = Link->ForwardLink;
3578 }
3579
3580 //
3581 // Move to the option in Next page.
3582 //
3583 if (TopOfScreen == &gMenuOption) {
3584 NewPos = gMenuOption.BackLink;
3585 MoveToNextStatement (Selection, TRUE, &NewPos, BottomRow - TopRow);
3586 } else {
3587 NewPos = Link;
3588 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
3589 }
3590
3591 //
3592 // There are more MenuOption needing scrolling up.
3593 //
3594 TopOfScreen = Link;
3595 MenuOption = NULL;
3596 }
3597
3598 //
3599 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3600 // Don't do this when we are already in the first page.
3601 //
3602 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3603 AdjustDateAndTimePosition (TRUE, &NewPos);
3604 break;
3605
3606 case CfUiPageDown:
3607 //
3608 // SkipValue means lines is skipped when show the top menu option.
3609 //
3610 ControlFlag = CfCheckSelection;
3611
3612 ASSERT (NewPos != NULL);
3613 if (NewPos->ForwardLink == &gMenuOption) {
3614 NewLine = FALSE;
3615 Repaint = FALSE;
3616 break;
3617 }
3618
3619 NewLine = TRUE;
3620 Repaint = TRUE;
3621 Link = TopOfScreen;
3622 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
3623 Index = TopRow + NextMenuOption->Skip - SkipValue;
3624 //
3625 // Count to the menu option which will show at the top of the next form.
3626 //
3627 while ((Index <= BottomRow + 1) && (Link->ForwardLink != &gMenuOption)) {
3628 Link = Link->ForwardLink;
3629 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
3630 Index = Index + NextMenuOption->Skip;
3631 }
3632
3633 if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow + 1)) {
3634 //
3635 // Finally we know that NewPos is the last MenuOption can be focused.
3636 //
3637 Repaint = FALSE;
3638 MoveToNextStatement (Selection, TRUE, &Link, Index - TopRow);
3639 SkipValue = 0;
3640 } else {
3641 //
3642 // Calculate the skip line for top of screen menu.
3643 //
3644 if (Link == TopOfScreen) {
3645 //
3646 // The top of screen menu option occupies the entire form.
3647 //
3648 SkipValue += BottomRow - TopRow + 1;
3649 } else {
3650 SkipValue = NextMenuOption->Skip - (Index - (BottomRow + 1));
3651 }
3652
3653 TopOfScreen = Link;
3654 MenuOption = NULL;
3655 //
3656 // Move to the Next selectable menu.
3657 //
3658 MoveToNextStatement (Selection, FALSE, &Link, BottomRow - TopRow);
3659 }
3660
3661 //
3662 // Save the menu as the next highlight menu.
3663 //
3664 NewPos = Link;
3665
3666 //
3667 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
3668 // Don't do this when we are already in the last page.
3669 //
3670 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3671 AdjustDateAndTimePosition (TRUE, &NewPos);
3672 break;
3673
3674 case CfUiDown:
3675 //
3676 // SkipValue means lines is skipped when show the top menu option.
3677 // NewPos points to the menu which is highlighted now.
3678 //
3679 ControlFlag = CfCheckSelection;
3680 //
3681 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
3682 // to be one that progresses to the next set of op-codes, we need to advance to the last
3683 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
3684 // checking can be done. The only other logic we need to introduce is that if a Date/Time
3685 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
3686 // the Date/Time op-code.
3687 //
3688 SavedListEntry = NewPos;
3689 AdjustDateAndTimePosition (FALSE, &NewPos);
3690
3691 if (NewPos->ForwardLink != &gMenuOption) {
3692 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
3693 NewLine = TRUE;
3694 NewPos = NewPos->ForwardLink;
3695
3696 Difference = 0;
3697 //
3698 // Current menu not at the bottom of the form.
3699 //
3700 if (BottomRow >= MenuOption->Row + MenuOption->Skip) {
3701 //
3702 // Find the next selectable menu.
3703 //
3704 Difference = MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - MenuOption->Row - MenuOption->Skip);
3705 //
3706 // We hit the end of MenuOption that can be focused
3707 // so we simply scroll to the first page.
3708 //
3709 if (Difference < 0) {
3710 //
3711 // Scroll to the first page.
3712 //
3713 if (TopOfScreen != gMenuOption.ForwardLink) {
3714 TopOfScreen = gMenuOption.ForwardLink;
3715 Repaint = TRUE;
3716 MenuOption = NULL;
3717 } else {
3718 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3719 }
3720 NewPos = gMenuOption.ForwardLink;
3721 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
3722
3723 SkipValue = 0;
3724 //
3725 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3726 //
3727 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3728 AdjustDateAndTimePosition (TRUE, &NewPos);
3729 break;
3730 }
3731 }
3732 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3733 if (NextMenuOption->Row == 0) {
3734 UpdateOptionSkipLines (Selection, NextMenuOption);
3735 }
3736 DistanceValue = Difference + NextMenuOption->Skip;
3737
3738 Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
3739 if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
3740 (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
3741 NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
3742 ) {
3743 Temp ++;
3744 }
3745
3746 //
3747 // If we are going to scroll, update TopOfScreen
3748 //
3749 if (Temp > BottomRow) {
3750 do {
3751 //
3752 // Is the current top of screen a zero-advance op-code?
3753 // If so, keep moving forward till we hit a >0 advance op-code
3754 //
3755 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3756
3757 //
3758 // If bottom op-code is more than one line or top op-code is more than one line
3759 //
3760 if ((DistanceValue > 1) || (SavedMenuOption->Skip > 1)) {
3761 //
3762 // Is the bottom op-code greater than or equal in size to the top op-code?
3763 //
3764 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - SkipValue)) {
3765 //
3766 // Skip the top op-code
3767 //
3768 TopOfScreen = TopOfScreen->ForwardLink;
3769 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - SkipValue);
3770
3771 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3772
3773 //
3774 // If we have a remainder, skip that many more op-codes until we drain the remainder
3775 //
3776 while (Difference >= (INTN) SavedMenuOption->Skip) {
3777 //
3778 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3779 //
3780 Difference = Difference - (INTN) SavedMenuOption->Skip;
3781 TopOfScreen = TopOfScreen->ForwardLink;
3782 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3783 }
3784 //
3785 // Since we will act on this op-code in the next routine, and increment the
3786 // SkipValue, set the skips to one less than what is required.
3787 //
3788 SkipValue = Difference - 1;
3789 } else {
3790 //
3791 // Since we will act on this op-code in the next routine, and increment the
3792 // SkipValue, set the skips to one less than what is required.
3793 //
3794 SkipValue += (Temp - BottomRow) - 1;
3795 }
3796 } else {
3797 if ((SkipValue + 1) == (INTN) SavedMenuOption->Skip) {
3798 TopOfScreen = TopOfScreen->ForwardLink;
3799 break;
3800 }
3801 }
3802 //
3803 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3804 // Let's set a skip flag to smoothly scroll the top of the screen.
3805 //
3806 if (SavedMenuOption->Skip > 1) {
3807 if (SavedMenuOption == NextMenuOption) {
3808 SkipValue = 0;
3809 } else {
3810 SkipValue++;
3811 }
3812 } else if (SavedMenuOption->Skip == 1) {
3813 SkipValue = 0;
3814 } else {
3815 SkipValue = 0;
3816 TopOfScreen = TopOfScreen->ForwardLink;
3817 }
3818 } while (SavedMenuOption->Skip == 0);
3819
3820 Repaint = TRUE;
3821 } else if (!IsSelectable (NextMenuOption)) {
3822 //
3823 // Continue to go down until scroll to next page or the selectable option is found.
3824 //
3825 ScreenOperation = UiDown;
3826 ControlFlag = CfScreenOperation;
3827 }
3828
3829 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3830
3831 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3832
3833 } else {
3834 //
3835 // Scroll to the first page.
3836 //
3837 if (TopOfScreen != gMenuOption.ForwardLink) {
3838 TopOfScreen = gMenuOption.ForwardLink;
3839 Repaint = TRUE;
3840 MenuOption = NULL;
3841 } else {
3842 //
3843 // Need to remove the current highlight menu.
3844 // MenuOption saved the last highlight menu info.
3845 //
3846 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3847 }
3848
3849 SkipValue = 0;
3850 NewLine = TRUE;
3851 //
3852 // Get the next highlight menu.
3853 //
3854 NewPos = gMenuOption.ForwardLink;
3855 MoveToNextStatement (Selection, FALSE, &NewPos, BottomRow - TopRow);
3856 }
3857
3858 //
3859 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3860 //
3861 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3862 AdjustDateAndTimePosition (TRUE, &NewPos);
3863 break;
3864
3865 case CfUiHotKey:
3866 ControlFlag = CfCheckSelection;
3867
3868 Status = EFI_SUCCESS;
3869 //
3870 // Discard changes. After it, no NV flag is showed.
3871 //
3872 if ((HotKey->Action & BROWSER_ACTION_DISCARD) == BROWSER_ACTION_DISCARD) {
3873 Status = DiscardForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
3874 if (!EFI_ERROR (Status)) {
3875 Selection->Action = UI_ACTION_REFRESH_FORM;
3876 Selection->Statement = NULL;
3877 gResetRequired = FALSE;
3878 } else {
3879 do {
3880 CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gDiscardFailed, gPressEnter, gEmptyString);
3881 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3882 //
3883 // Still show current page.
3884 //
3885 Selection->Action = UI_ACTION_NONE;
3886 Repaint = TRUE;
3887 NewLine = TRUE;
3888 break;
3889 }
3890 }
3891
3892 //
3893 // Reterieve default setting. After it. NV flag will be showed.
3894 //
3895 if ((HotKey->Action & BROWSER_ACTION_DEFAULT) == BROWSER_ACTION_DEFAULT) {
3896 Status = ExtractDefault (Selection->FormSet, Selection->Form, HotKey->DefaultId, gBrowserSettingScope, GetDefaultForAll, NULL, FALSE);
3897 if (!EFI_ERROR (Status)) {
3898 Selection->Action = UI_ACTION_REFRESH_FORM;
3899 Selection->Statement = NULL;
3900 gResetRequired = TRUE;
3901 } else {
3902 do {
3903 CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gDefaultFailed, gPressEnter, gEmptyString);
3904 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3905 //
3906 // Still show current page.
3907 //
3908 Selection->Action = UI_ACTION_NONE;
3909 Repaint = TRUE;
3910 NewLine = TRUE;
3911 break;
3912 }
3913 }
3914
3915 //
3916 // Save changes. After it, no NV flag is showed.
3917 //
3918 if ((HotKey->Action & BROWSER_ACTION_SUBMIT) == BROWSER_ACTION_SUBMIT) {
3919 Status = SubmitForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
3920 if (!EFI_ERROR (Status)) {
3921 ASSERT(MenuOption != NULL);
3922 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3923 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
3924 } else {
3925 do {
3926 CreateDialog (4, TRUE, 0, NULL, &Key, HotKey->HelpString, gSaveFailed, gPressEnter, gEmptyString);
3927 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3928 //
3929 // Still show current page.
3930 //
3931 Selection->Action = UI_ACTION_NONE;
3932 Repaint = TRUE;
3933 NewLine = TRUE;
3934 break;
3935 }
3936 }
3937
3938 //
3939 // Set Reset required Flag
3940 //
3941 if ((HotKey->Action & BROWSER_ACTION_RESET) == BROWSER_ACTION_RESET) {
3942 gResetRequired = TRUE;
3943 }
3944
3945 //
3946 // Exit Action
3947 //
3948 if ((HotKey->Action & BROWSER_ACTION_EXIT) == BROWSER_ACTION_EXIT) {
3949 //
3950 // Form Exit without saving, Similar to ESC Key.
3951 // FormSet Exit without saving, Exit SendForm.
3952 // System Exit without saving, CallExitHandler and Exit SendForm.
3953 //
3954 DiscardForm (Selection->FormSet, Selection->Form, gBrowserSettingScope);
3955 if (gBrowserSettingScope == FormLevel) {
3956 ControlFlag = CfUiReset;
3957 } else if (gBrowserSettingScope == FormSetLevel) {
3958 Selection->Action = UI_ACTION_EXIT;
3959 } else if (gBrowserSettingScope == SystemLevel) {
3960 if (ExitHandlerFunction != NULL) {
3961 ExitHandlerFunction ();
3962 }
3963 Selection->Action = UI_ACTION_EXIT;
3964 }
3965 Selection->Statement = NULL;
3966 }
3967 break;
3968
3969 case CfUiDefault:
3970 ControlFlag = CfCheckSelection;
3971 //
3972 // Reset to default value for all forms in the whole system.
3973 //
3974 Status = ExtractDefault (Selection->FormSet, NULL, DefaultId, FormSetLevel, GetDefaultForAll, NULL, FALSE);
3975
3976 if (!EFI_ERROR (Status)) {
3977 Selection->Action = UI_ACTION_REFRESH_FORM;
3978 Selection->Statement = NULL;
3979 gResetRequired = TRUE;
3980 }
3981 break;
3982
3983 case CfUiNoOperation:
3984 ControlFlag = CfCheckSelection;
3985 break;
3986
3987 case CfExit:
3988 UiFreeRefreshList ();
3989
3990 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
3991 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
3992 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
3993 gST->ConOut->OutputString (gST->ConOut, L"\n");
3994 if (HelpString != NULL) {
3995 FreePool (HelpString);
3996 }
3997 if (HelpHeaderString != NULL) {
3998 FreePool (HelpHeaderString);
3999 }
4000 if (HelpBottomString != NULL) {
4001 FreePool (HelpBottomString);
4002 }
4003
4004 return EFI_SUCCESS;
4005
4006 default:
4007 break;
4008 }
4009 }
4010 }