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