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