]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
Sync in bug fix from EDK I:
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / Ui.c
1 /** @file
2 Utility functions for User Interface functions.
3
4 Copyright (c) 2004 - 2008, Intel Corporation
5 All rights reserved. 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 "Ui.h"
16 #include "Setup.h"
17
18 LIST_ENTRY Menu;
19 LIST_ENTRY gMenuList;
20 MENU_REFRESH_ENTRY *gMenuRefreshHead;
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_F2,
48 UiPrevious,
49 },
50 {
51 SCAN_LEFT,
52 UiLeft,
53 },
54 {
55 SCAN_RIGHT,
56 UiRight,
57 },
58 {
59 SCAN_F9,
60 UiDefault,
61 },
62 {
63 SCAN_F10,
64 UiSave
65 }
66 };
67
68 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
69 {
70 UiNoOperation,
71 CfUiNoOperation,
72 },
73 {
74 UiDefault,
75 CfUiDefault,
76 },
77 {
78 UiSelect,
79 CfUiSelect,
80 },
81 {
82 UiUp,
83 CfUiUp,
84 },
85 {
86 UiDown,
87 CfUiDown,
88 },
89 {
90 UiLeft,
91 CfUiLeft,
92 },
93 {
94 UiRight,
95 CfUiRight,
96 },
97 {
98 UiReset,
99 CfUiReset,
100 },
101 {
102 UiSave,
103 CfUiSave,
104 },
105 {
106 UiPrevious,
107 CfUiPrevious,
108 },
109 {
110 UiPageUp,
111 CfUiPageUp,
112 },
113 {
114 UiPageDown,
115 CfUiPageDown
116 }
117 };
118
119 BOOLEAN mInputError;
120
121
122 /**
123 Set Buffer to Value for Size bytes.
124
125 @param Buffer Memory to set.
126 @param Size Number of bytes to set
127 @param Value Value of the set operation.
128
129 @return Value.
130
131 **/
132 VOID
133 SetUnicodeMem (
134 IN VOID *Buffer,
135 IN UINTN Size,
136 IN CHAR16 Value
137 )
138 {
139 CHAR16 *Ptr;
140
141 Ptr = Buffer;
142 while ((Size--) != 0) {
143 *(Ptr++) = Value;
144 }
145 }
146
147
148 /**
149 Initialize Menu option list.
150
151 **/
152 VOID
153 UiInitMenu (
154 VOID
155 )
156 {
157 InitializeListHead (&Menu);
158 }
159
160
161 /**
162 Initialize Menu option list.
163
164 **/
165 VOID
166 UiInitMenuList (
167 VOID
168 )
169 {
170 InitializeListHead (&gMenuList);
171 }
172
173
174 /**
175 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
176
177 @param Selection Menu selection.
178
179 **/
180 VOID
181 UiRemoveMenuListEntry (
182 OUT UI_MENU_SELECTION *Selection
183 )
184 {
185 UI_MENU_LIST *UiMenuList;
186
187 if (!IsListEmpty (&gMenuList)) {
188 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
189
190 Selection->FormId = UiMenuList->FormId;
191 Selection->QuestionId = UiMenuList->QuestionId;
192 RemoveEntryList (&UiMenuList->MenuLink);
193 gBS->FreePool (UiMenuList);
194 }
195 }
196
197
198 /**
199 Free Menu option linked list.
200
201 **/
202 VOID
203 UiFreeMenuList (
204 VOID
205 )
206 {
207 UI_MENU_LIST *UiMenuList;
208
209 while (!IsListEmpty (&gMenuList)) {
210 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
211 RemoveEntryList (&UiMenuList->MenuLink);
212 gBS->FreePool (UiMenuList);
213 }
214 }
215
216
217 /**
218 Add one menu entry to the linked lst
219
220 @param Selection Menu selection.
221
222 **/
223 VOID
224 UiAddMenuListEntry (
225 IN UI_MENU_SELECTION *Selection
226 )
227 {
228 UI_MENU_LIST *UiMenuList;
229
230 UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
231 ASSERT (UiMenuList != NULL);
232
233 UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
234 UiMenuList->FormId = Selection->FormId;
235 UiMenuList->QuestionId = Selection->QuestionId;
236
237 InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
238 }
239
240
241 /**
242 Free Menu option linked list.
243
244 **/
245 VOID
246 UiFreeMenu (
247 VOID
248 )
249 {
250 UI_MENU_OPTION *MenuOption;
251
252 while (!IsListEmpty (&Menu)) {
253 MenuOption = MENU_OPTION_FROM_LINK (Menu.ForwardLink);
254 RemoveEntryList (&MenuOption->Link);
255
256 //
257 // We allocated space for this description when we did a GetToken, free it here
258 //
259 if (MenuOption->Skip != 0) {
260 //
261 // For date/time, MenuOption->Description is shared by three Menu Options
262 // Data format : [01/02/2004] [11:22:33]
263 // Line number : 0 0 1 0 0 1
264 //
265 gBS->FreePool (MenuOption->Description);
266 }
267 gBS->FreePool (MenuOption);
268 }
269 }
270
271
272 /**
273 Free Menu option linked list.
274
275 **/
276 VOID
277 UiFreeRefreshList (
278 VOID
279 )
280 {
281 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
282
283 while (gMenuRefreshHead != NULL) {
284 OldMenuRefreshEntry = gMenuRefreshHead->Next;
285 gBS->FreePool (gMenuRefreshHead);
286 gMenuRefreshHead = OldMenuRefreshEntry;
287 }
288
289 gMenuRefreshHead = NULL;
290 }
291
292
293
294 /**
295 Refresh screen.
296
297 **/
298 EFI_STATUS
299 RefreshForm (
300 VOID
301 )
302 {
303 CHAR16 *OptionString;
304 MENU_REFRESH_ENTRY *MenuRefreshEntry;
305 UINTN Index;
306 EFI_STATUS Status;
307 UI_MENU_SELECTION *Selection;
308 FORM_BROWSER_STATEMENT *Question;
309 EFI_HII_CONFIG_ACCESS_PROTOCOL *ConfigAccess;
310 EFI_HII_VALUE *HiiValue;
311 EFI_BROWSER_ACTION_REQUEST ActionRequest;
312
313 if (gMenuRefreshHead != NULL) {
314
315 MenuRefreshEntry = gMenuRefreshHead;
316
317 //
318 // Reset FormPackage update flag
319 //
320 mHiiPackageListUpdated = FALSE;
321
322 do {
323 gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
324
325 Selection = MenuRefreshEntry->Selection;
326 Question = MenuRefreshEntry->MenuOption->ThisTag;
327
328 Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
329 if (EFI_ERROR (Status)) {
330 return Status;
331 }
332
333 OptionString = NULL;
334 ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
335
336 if (OptionString != NULL) {
337 //
338 // If leading spaces on OptionString - remove the spaces
339 //
340 for (Index = 0; OptionString[Index] == L' '; Index++)
341 ;
342
343 PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, &OptionString[Index]);
344 gBS->FreePool (OptionString);
345 }
346
347 //
348 // Question value may be changed, need invoke its Callback()
349 //
350 ConfigAccess = Selection->FormSet->ConfigAccess;
351 if ((Question->QuestionFlags & EFI_IFR_FLAG_CALLBACK) && (ConfigAccess != NULL)) {
352 ActionRequest = EFI_BROWSER_ACTION_REQUEST_NONE;
353
354 HiiValue = &Question->HiiValue;
355 if (HiiValue->Type == EFI_IFR_TYPE_STRING) {
356 //
357 // Create String in HII database for Configuration Driver to retrieve
358 //
359 HiiValue->Value.string = NewString ((CHAR16 *) Question->BufferValue, Selection->FormSet->HiiHandle);
360 }
361
362 Status = ConfigAccess->Callback (
363 ConfigAccess,
364 EFI_BROWSER_ACTION_CHANGING,
365 Question->QuestionId,
366 HiiValue->Type,
367 &HiiValue->Value,
368 &ActionRequest
369 );
370
371 if (HiiValue->Type == EFI_IFR_TYPE_STRING) {
372 //
373 // Clean the String in HII Database
374 //
375 DeleteString (HiiValue->Value.string, Selection->FormSet->HiiHandle);
376 }
377
378 if (!EFI_ERROR (Status)) {
379 switch (ActionRequest) {
380 case EFI_BROWSER_ACTION_REQUEST_RESET:
381 gResetRequired = TRUE;
382 break;
383
384 case EFI_BROWSER_ACTION_REQUEST_SUBMIT:
385 SubmitForm (Selection->FormSet, Selection->Form);
386 break;
387
388 case EFI_BROWSER_ACTION_REQUEST_EXIT:
389 Selection->Action = UI_ACTION_EXIT;
390 gNvUpdateRequired = FALSE;
391 break;
392
393 default:
394 break;
395 }
396 }
397 }
398
399 MenuRefreshEntry = MenuRefreshEntry->Next;
400
401 } while (MenuRefreshEntry != NULL);
402
403 if (mHiiPackageListUpdated) {
404 //
405 // Package list is updated, force to reparse IFR binary of target Formset
406 //
407 mHiiPackageListUpdated = FALSE;
408 Selection->Action = UI_ACTION_REFRESH_FORMSET;
409 return EFI_SUCCESS;
410 }
411 }
412
413 return EFI_TIMEOUT;
414 }
415
416
417 /**
418 Wait for a given event to fire, or for an optional timeout to expire.
419
420 @param Event The event to wait for
421 @param Timeout An optional timeout value in 100 ns units.
422 @param RefreshInterval Menu refresh interval (in seconds).
423
424 @retval EFI_SUCCESS Event fired before Timeout expired.
425 @retval EFI_TIME_OUT Timout expired before Event fired.
426
427 **/
428 EFI_STATUS
429 UiWaitForSingleEvent (
430 IN EFI_EVENT Event,
431 IN UINT64 Timeout, OPTIONAL
432 IN UINT8 RefreshInterval OPTIONAL
433 )
434 {
435 EFI_STATUS Status;
436 UINTN Index;
437 EFI_EVENT TimerEvent;
438 EFI_EVENT WaitList[2];
439
440 if (Timeout != 0) {
441 //
442 // Create a timer event
443 //
444 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
445 if (!EFI_ERROR (Status)) {
446 //
447 // Set the timer event
448 //
449 gBS->SetTimer (
450 TimerEvent,
451 TimerRelative,
452 Timeout
453 );
454
455 //
456 // Wait for the original event or the timer
457 //
458 WaitList[0] = Event;
459 WaitList[1] = TimerEvent;
460 Status = gBS->WaitForEvent (2, WaitList, &Index);
461 gBS->CloseEvent (TimerEvent);
462
463 //
464 // If the timer expired, change the return to timed out
465 //
466 if (!EFI_ERROR (Status) && Index == 1) {
467 Status = EFI_TIMEOUT;
468 }
469 }
470 } else {
471 //
472 // Update screen every second
473 //
474 if (RefreshInterval == 0) {
475 Timeout = ONE_SECOND;
476 } else {
477 Timeout = RefreshInterval * ONE_SECOND;
478 }
479
480 do {
481 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
482
483 //
484 // Set the timer event
485 //
486 gBS->SetTimer (
487 TimerEvent,
488 TimerRelative,
489 Timeout
490 );
491
492 //
493 // Wait for the original event or the timer
494 //
495 WaitList[0] = Event;
496 WaitList[1] = TimerEvent;
497 Status = gBS->WaitForEvent (2, WaitList, &Index);
498
499 //
500 // If the timer expired, update anything that needs a refresh and keep waiting
501 //
502 if (!EFI_ERROR (Status) && Index == 1) {
503 Status = EFI_TIMEOUT;
504 if (RefreshInterval != 0) {
505 Status = RefreshForm ();
506 }
507 }
508
509 gBS->CloseEvent (TimerEvent);
510 } while (Status == EFI_TIMEOUT);
511 }
512
513 return Status;
514 }
515
516
517 /**
518 Add one menu option by specified description and context.
519
520 @param String String description for this option.
521 @param Handle Hii handle for the package list.
522 @param Statement Statement of this Menu Option.
523 @param NumberOfLines Display lines for this Menu Option.
524 @param MenuItemCount The index for this Option in the Menu.
525
526 **/
527 VOID
528 UiAddMenuOption (
529 IN CHAR16 *String,
530 IN EFI_HII_HANDLE Handle,
531 IN FORM_BROWSER_STATEMENT *Statement,
532 IN UINT16 NumberOfLines,
533 IN UINT16 MenuItemCount
534 )
535 {
536 UI_MENU_OPTION *MenuOption;
537 UINTN Index;
538 UINTN Count;
539
540 Count = 1;
541
542 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
543 //
544 // Add three MenuOptions for Date/Time
545 // Data format : [01/02/2004] [11:22:33]
546 // Line number : 0 0 1 0 0 1
547 //
548 NumberOfLines = 0;
549 Count = 3;
550
551 if (Statement->Storage == NULL) {
552 //
553 // For RTC type of date/time, set default refresh interval to be 1 second
554 //
555 if (Statement->RefreshInterval == 0) {
556 Statement->RefreshInterval = 1;
557 }
558 }
559 }
560
561 for (Index = 0; Index < Count; Index++) {
562 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
563 ASSERT (MenuOption);
564
565 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
566 MenuOption->Description = String;
567 MenuOption->Handle = Handle;
568 MenuOption->ThisTag = Statement;
569 MenuOption->EntryNumber = MenuItemCount;
570
571 if (Index == 2) {
572 //
573 // Override LineNumber for the MenuOption in Date/Time sequence
574 //
575 MenuOption->Skip = 1;
576 } else {
577 MenuOption->Skip = NumberOfLines;
578 }
579 MenuOption->Sequence = Index;
580
581 if (Statement->GrayOutExpression != NULL) {
582 MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b;
583 }
584
585 if ((Statement->ValueExpression != NULL) ||
586 ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) {
587 MenuOption->ReadOnly = TRUE;
588 }
589
590 InsertTailList (&Menu, &MenuOption->Link);
591 }
592 }
593
594
595 /**
596 Routine used to abstract a generic dialog interface and return the selected key or string
597
598 @param NumberOfLines The number of lines for the dialog box
599 @param HotKey Defines whether a single character is parsed
600 (TRUE) and returned in KeyValue or a string is
601 returned in StringBuffer. Two special characters
602 are considered when entering a string, a SCAN_ESC
603 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
604 string input and returns
605 @param MaximumStringSize The maximum size in bytes of a typed in string
606 (each character is a CHAR16) and the minimum
607 string returned is two bytes
608 @param StringBuffer The passed in pointer to the buffer which will
609 hold the typed in string if HotKey is FALSE
610 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
611 @param ... A series of (quantity == NumberOfLines) text
612 strings which will be used to construct the dialog
613 box
614
615 @retval EFI_SUCCESS Displayed dialog and received user interaction
616 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
617 (StringBuffer == NULL) && (HotKey == FALSE))
618 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
619
620 **/
621 EFI_STATUS
622 CreateDialog (
623 IN UINTN NumberOfLines,
624 IN BOOLEAN HotKey,
625 IN UINTN MaximumStringSize,
626 OUT CHAR16 *StringBuffer,
627 OUT EFI_INPUT_KEY *KeyValue,
628 ...
629 )
630 {
631 VA_LIST Marker;
632 VA_LIST MarkerBackup;
633 UINTN Count;
634 EFI_INPUT_KEY Key;
635 UINTN LargestString;
636 CHAR16 *TempString;
637 CHAR16 *BufferedString;
638 CHAR16 *StackString;
639 CHAR16 KeyPad[2];
640 UINTN Start;
641 UINTN Top;
642 UINTN Index;
643 EFI_STATUS Status;
644 BOOLEAN SelectionComplete;
645 UINTN InputOffset;
646 UINTN CurrentAttribute;
647 UINTN DimensionsWidth;
648 UINTN DimensionsHeight;
649
650 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
651 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
652
653 SelectionComplete = FALSE;
654 InputOffset = 0;
655 TempString = AllocateZeroPool (MaximumStringSize * 2);
656 BufferedString = AllocateZeroPool (MaximumStringSize * 2);
657 CurrentAttribute = gST->ConOut->Mode->Attribute;
658
659 ASSERT (TempString);
660 ASSERT (BufferedString);
661
662 VA_START (Marker, KeyValue);
663 MarkerBackup = Marker;
664
665 //
666 // Zero the outgoing buffer
667 //
668 ZeroMem (StringBuffer, MaximumStringSize);
669
670 if (HotKey) {
671 if (KeyValue == NULL) {
672 return EFI_INVALID_PARAMETER;
673 }
674 } else {
675 if (StringBuffer == NULL) {
676 return EFI_INVALID_PARAMETER;
677 }
678 }
679 //
680 // Disable cursor
681 //
682 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
683
684 LargestString = 0;
685
686 //
687 // Determine the largest string in the dialog box
688 // Notice we are starting with 1 since String is the first string
689 //
690 for (Count = 0; Count < NumberOfLines; Count++) {
691 StackString = VA_ARG (Marker, CHAR16 *);
692
693 if (StackString[0] == L' ') {
694 InputOffset = Count + 1;
695 }
696
697 if ((GetStringWidth (StackString) / 2) > LargestString) {
698 //
699 // Size of the string visually and subtract the width by one for the null-terminator
700 //
701 LargestString = (GetStringWidth (StackString) / 2);
702 }
703 }
704
705 Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
706 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
707
708 Count = 0;
709
710 //
711 // Display the Popup
712 //
713 CreateSharedPopUp (LargestString, NumberOfLines, MarkerBackup);
714
715 //
716 // Take the first key typed and report it back?
717 //
718 if (HotKey) {
719 Status = WaitForKeyStroke (&Key);
720 ASSERT_EFI_ERROR (Status);
721 CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
722
723 } else {
724 do {
725 Status = WaitForKeyStroke (&Key);
726
727 switch (Key.UnicodeChar) {
728 case CHAR_NULL:
729 switch (Key.ScanCode) {
730 case SCAN_ESC:
731 gBS->FreePool (TempString);
732 gBS->FreePool (BufferedString);
733 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
734 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
735 return EFI_DEVICE_ERROR;
736
737 default:
738 break;
739 }
740
741 break;
742
743 case CHAR_CARRIAGE_RETURN:
744 SelectionComplete = TRUE;
745 gBS->FreePool (TempString);
746 gBS->FreePool (BufferedString);
747 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
748 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
749 return EFI_SUCCESS;
750 break;
751
752 case CHAR_BACKSPACE:
753 if (StringBuffer[0] != CHAR_NULL) {
754 for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
755 TempString[Index] = StringBuffer[Index];
756 }
757 //
758 // Effectively truncate string by 1 character
759 //
760 TempString[Index - 1] = CHAR_NULL;
761 StrCpy (StringBuffer, TempString);
762 }
763
764 default:
765 //
766 // If it is the beginning of the string, don't worry about checking maximum limits
767 //
768 if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
769 StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
770 StrnCpy (TempString, &Key.UnicodeChar, 1);
771 } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
772 KeyPad[0] = Key.UnicodeChar;
773 KeyPad[1] = CHAR_NULL;
774 StrCat (StringBuffer, KeyPad);
775 StrCat (TempString, KeyPad);
776 }
777 //
778 // If the width of the input string is now larger than the screen, we nee to
779 // adjust the index to start printing portions of the string
780 //
781 SetUnicodeMem (BufferedString, LargestString, L' ');
782
783 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
784
785 if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
786 Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
787 } else {
788 Index = 0;
789 }
790
791 for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
792 BufferedString[Count] = StringBuffer[Index];
793 }
794
795 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
796 break;
797 }
798 } while (!SelectionComplete);
799 }
800
801 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
802 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
803 return EFI_SUCCESS;
804 }
805
806 /**
807 Draw a pop up windows based on the dimension, number of lines and
808 strings specified.
809
810 @param RequestedWidth The width of the pop-up.
811 @param NumberOfLines The number of lines.
812 @param Marker The variable argument list for the list of string to be printed.
813
814 **/
815 VOID
816 CreateSharedPopUp (
817 IN UINTN RequestedWidth,
818 IN UINTN NumberOfLines,
819 IN VA_LIST Marker
820 )
821 {
822 UINTN Index;
823 UINTN Count;
824 CHAR16 Character;
825 UINTN Start;
826 UINTN End;
827 UINTN Top;
828 UINTN Bottom;
829 CHAR16 *String;
830 UINTN DimensionsWidth;
831 UINTN DimensionsHeight;
832
833 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
834 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
835
836 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
837
838 if ((RequestedWidth + 2) > DimensionsWidth) {
839 RequestedWidth = DimensionsWidth - 2;
840 }
841
842 //
843 // Subtract the PopUp width from total Columns, allow for one space extra on
844 // each end plus a border.
845 //
846 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
847 End = Start + RequestedWidth + 1;
848
849 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
850 Bottom = Top + NumberOfLines + 2;
851
852 Character = BOXDRAW_DOWN_RIGHT;
853 PrintCharAt (Start, Top, Character);
854 Character = BOXDRAW_HORIZONTAL;
855 for (Index = Start; Index + 2 < End; Index++) {
856 PrintChar (Character);
857 }
858
859 Character = BOXDRAW_DOWN_LEFT;
860 PrintChar (Character);
861 Character = BOXDRAW_VERTICAL;
862
863 Count = 0;
864 for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
865 String = VA_ARG (Marker, CHAR16*);
866
867 //
868 // This will clear the background of the line - we never know who might have been
869 // here before us. This differs from the next clear in that it used the non-reverse
870 // video for normal printing.
871 //
872 if (GetStringWidth (String) / 2 > 1) {
873 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
874 }
875
876 //
877 // Passing in a space results in the assumption that this is where typing will occur
878 //
879 if (String[0] == L' ') {
880 ClearLines (Start + 1, End - 1, Index + 1, Index + 1, POPUP_INVERSE_TEXT | POPUP_INVERSE_BACKGROUND);
881 }
882
883 //
884 // Passing in a NULL results in a blank space
885 //
886 if (String[0] == CHAR_NULL) {
887 ClearLines (Start, End, Index + 1, Index + 1, POPUP_TEXT | POPUP_BACKGROUND);
888 }
889
890 PrintStringAt (
891 ((DimensionsWidth - GetStringWidth (String) / 2) / 2) + gScreenDimensions.LeftColumn + 1,
892 Index + 1,
893 String
894 );
895 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
896 PrintCharAt (Start, Index + 1, Character);
897 PrintCharAt (End - 1, Index + 1, Character);
898 }
899
900 Character = BOXDRAW_UP_RIGHT;
901 PrintCharAt (Start, Bottom - 1, Character);
902 Character = BOXDRAW_HORIZONTAL;
903 for (Index = Start; Index + 2 < End; Index++) {
904 PrintChar (Character);
905 }
906
907 Character = BOXDRAW_UP_LEFT;
908 PrintChar (Character);
909 }
910
911 /**
912 Draw a pop up windows based on the dimension, number of lines and
913 strings specified.
914
915 @param RequestedWidth The width of the pop-up.
916 @param NumberOfLines The number of lines.
917 @param ... A series of text strings that displayed in the pop-up.
918
919 **/
920 VOID
921 CreatePopUp (
922 IN UINTN RequestedWidth,
923 IN UINTN NumberOfLines,
924 ...
925 )
926 {
927 VA_LIST Marker;
928
929 VA_START (Marker, NumberOfLines);
930
931 CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
932
933 VA_END (Marker);
934 }
935
936
937 /**
938 Update status bar on the bottom of menu.
939
940 @param MessageType The type of message to be shown.
941 @param Flags The flags in Question header.
942 @param State Set or clear.
943
944 **/
945 VOID
946 UpdateStatusBar (
947 IN UINTN MessageType,
948 IN UINT8 Flags,
949 IN BOOLEAN State
950 )
951 {
952 UINTN Index;
953 CHAR16 *NvUpdateMessage;
954 CHAR16 *InputErrorMessage;
955
956 NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
957 InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
958
959 switch (MessageType) {
960 case INPUT_ERROR:
961 if (State) {
962 gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
963 PrintStringAt (
964 gScreenDimensions.LeftColumn + gPromptBlockWidth,
965 gScreenDimensions.BottomRow - 1,
966 InputErrorMessage
967 );
968 mInputError = TRUE;
969 } else {
970 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
971 for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
972 PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" ");
973 }
974
975 mInputError = FALSE;
976 }
977 break;
978
979 case NV_UPDATE_REQUIRED:
980 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
981 if (State) {
982 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
983 PrintStringAt (
984 gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
985 gScreenDimensions.BottomRow - 1,
986 NvUpdateMessage
987 );
988 gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
989
990 gNvUpdateRequired = TRUE;
991 } else {
992 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
993 for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
994 PrintAt (
995 (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
996 gScreenDimensions.BottomRow - 1,
997 L" "
998 );
999 }
1000
1001 gNvUpdateRequired = FALSE;
1002 }
1003 }
1004 break;
1005
1006 case REFRESH_STATUS_BAR:
1007 if (mInputError) {
1008 UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
1009 }
1010
1011 if (gNvUpdateRequired) {
1012 UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
1013 }
1014 break;
1015
1016 default:
1017 break;
1018 }
1019
1020 gBS->FreePool (InputErrorMessage);
1021 gBS->FreePool (NvUpdateMessage);
1022 return ;
1023 }
1024
1025
1026 /**
1027 Get the supported width for a particular op-code
1028
1029 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
1030 @param Handle The handle in the HII database being used
1031
1032 @return Returns the number of CHAR16 characters that is support.
1033
1034 **/
1035 UINT16
1036 GetWidth (
1037 IN FORM_BROWSER_STATEMENT *Statement,
1038 IN EFI_HII_HANDLE Handle
1039 )
1040 {
1041 CHAR16 *String;
1042 UINTN Size;
1043 UINT16 Width;
1044
1045 Size = 0;
1046
1047 //
1048 // See if the second text parameter is really NULL
1049 //
1050 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1051 String = GetToken (Statement->TextTwo, Handle);
1052 Size = StrLen (String);
1053 gBS->FreePool (String);
1054 }
1055
1056 if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
1057 (Statement->Operand == EFI_IFR_REF_OP) ||
1058 (Statement->Operand == EFI_IFR_PASSWORD_OP) ||
1059 (Statement->Operand == EFI_IFR_ACTION_OP) ||
1060 (Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
1061 //
1062 // Allow a wide display if text op-code and no secondary text op-code
1063 //
1064 ((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
1065 ) {
1066 Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
1067 } else {
1068 Width = (UINT16) gPromptBlockWidth;
1069 }
1070
1071 if (Statement->InSubtitle) {
1072 Width -= SUBTITLE_INDENT;
1073 }
1074
1075 return Width;
1076 }
1077
1078
1079 BOOLEAN GetLineByWidthFinished = FALSE;
1080
1081 /**
1082 Will copy LineWidth amount of a string in the OutputString buffer and return the
1083 number of CHAR16 characters that were copied into the OutputString buffer.
1084
1085 @param InputString String description for this option.
1086 @param LineWidth Width of the desired string to extract in CHAR16
1087 characters
1088 @param Index Where in InputString to start the copy process
1089 @param OutputString Buffer to copy the string into
1090
1091 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1092
1093 **/
1094 UINT16
1095 GetLineByWidth (
1096 IN CHAR16 *InputString,
1097 IN UINT16 LineWidth,
1098 IN OUT UINTN *Index,
1099 OUT CHAR16 **OutputString
1100 )
1101 {
1102 UINT16 Count;
1103 UINT16 Count2;
1104
1105 if (GetLineByWidthFinished) {
1106 GetLineByWidthFinished = FALSE;
1107 return (UINT16) 0;
1108 }
1109
1110 Count = LineWidth;
1111 Count2 = 0;
1112
1113 *OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
1114
1115 //
1116 // Ensure we have got a valid buffer
1117 //
1118 if (*OutputString != NULL) {
1119
1120 //
1121 //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.
1122 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1123 //
1124 if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
1125 *Index = *Index + 2;
1126 }
1127
1128 //
1129 // Fast-forward the string and see if there is a carriage-return in the string
1130 //
1131 for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
1132 ;
1133
1134 //
1135 // Copy the desired LineWidth of data to the output buffer.
1136 // Also make sure that we don't copy more than the string.
1137 // Also make sure that if there are linefeeds, we account for them.
1138 //
1139 if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
1140 (StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
1141 ) {
1142 //
1143 // Convert to CHAR16 value and show that we are done with this operation
1144 //
1145 LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
1146 if (LineWidth != 0) {
1147 GetLineByWidthFinished = TRUE;
1148 }
1149 } else {
1150 if (Count2 == LineWidth) {
1151 //
1152 // Rewind the string from the maximum size until we see a space to break the line
1153 //
1154 for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
1155 ;
1156 if (LineWidth == 0) {
1157 LineWidth = Count;
1158 }
1159 } else {
1160 LineWidth = Count2;
1161 }
1162 }
1163
1164 CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
1165
1166 //
1167 // If currently pointing to a space, increment the index to the first non-space character
1168 //
1169 for (;
1170 (InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
1171 (*Index)++
1172 )
1173 ;
1174 *Index = (UINT16) (*Index + LineWidth);
1175 return LineWidth;
1176 } else {
1177 return (UINT16) 0;
1178 }
1179 }
1180
1181
1182 /**
1183 Update display lines for a Menu Option.
1184
1185 @param Selection The user's selection.
1186 @param MenuOption The MenuOption to be checked.
1187 @param OptionalString The option string.
1188 @param SkipValue The number of lins to skip.
1189
1190 **/
1191 VOID
1192 UpdateOptionSkipLines (
1193 IN UI_MENU_SELECTION *Selection,
1194 IN UI_MENU_OPTION *MenuOption,
1195 OUT CHAR16 **OptionalString,
1196 IN UINTN SkipValue
1197 )
1198 {
1199 UINTN Index;
1200 UINT16 Width;
1201 UINTN Row;
1202 UINTN OriginalRow;
1203 CHAR16 *OutputString;
1204 CHAR16 *OptionString;
1205
1206 Row = 0;
1207 OptionString = *OptionalString;
1208 OutputString = NULL;
1209
1210 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1211
1212 if (OptionString != NULL) {
1213 Width = (UINT16) gOptionBlockWidth;
1214
1215 OriginalRow = Row;
1216
1217 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1218 //
1219 // If there is more string to process print on the next row and increment the Skip value
1220 //
1221 if (StrLen (&OptionString[Index])) {
1222 if (SkipValue == 0) {
1223 Row++;
1224 //
1225 // Since the Number of lines for this menu entry may or may not be reflected accurately
1226 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1227 // some testing to ensure we are keeping this in-sync.
1228 //
1229 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1230 //
1231 if ((Row - OriginalRow) >= MenuOption->Skip) {
1232 MenuOption->Skip++;
1233 }
1234 }
1235 }
1236
1237 gBS->FreePool (OutputString);
1238 if (SkipValue != 0) {
1239 SkipValue--;
1240 }
1241 }
1242
1243 Row = OriginalRow;
1244 }
1245
1246 *OptionalString = OptionString;
1247 }
1248
1249
1250 /**
1251 Check whether this Menu Option could be highlighted.
1252
1253 This is an internal function.
1254
1255 @param MenuOption The MenuOption to be checked.
1256
1257 @retval TRUE This Menu Option is selectable.
1258 @retval FALSE This Menu Option could not be selected.
1259
1260 **/
1261 BOOLEAN
1262 IsSelectable (
1263 UI_MENU_OPTION *MenuOption
1264 )
1265 {
1266 if ((MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) ||
1267 MenuOption->GrayOut || MenuOption->ReadOnly) {
1268 return FALSE;
1269 } else {
1270 return TRUE;
1271 }
1272 }
1273
1274
1275 /**
1276 Determine if the menu is the last menu that can be selected.
1277
1278 This is an internal function.
1279
1280 @param Direction The scroll direction. False is down. True is up.
1281 @param CurrentPos The current focus.
1282
1283 @return FALSE -- the menu isn't the last menu that can be selected.
1284 @return TRUE -- the menu is the last menu that can be selected.
1285
1286 **/
1287 BOOLEAN
1288 ValueIsScroll (
1289 IN BOOLEAN Direction,
1290 IN LIST_ENTRY *CurrentPos
1291 )
1292 {
1293 LIST_ENTRY *Temp;
1294 UI_MENU_OPTION *MenuOption;
1295
1296 Temp = Direction ? CurrentPos->BackLink : CurrentPos->ForwardLink;
1297
1298 if (Temp == &Menu) {
1299 return TRUE;
1300 }
1301
1302 for (; Temp != &Menu; Temp = Direction ? Temp->BackLink : Temp->ForwardLink) {
1303 MenuOption = MENU_OPTION_FROM_LINK (Temp);
1304 if (IsSelectable (MenuOption)) {
1305 return FALSE;
1306 }
1307 }
1308
1309 return TRUE;
1310 }
1311
1312
1313 /**
1314 Move to next selectable statement.
1315
1316 This is an internal function.
1317
1318 @param GoUp The navigation direction. TRUE: up, FALSE: down.
1319 @param CurrentPosition Current position.
1320
1321 @return The row distance from current MenuOption to next selectable MenuOption.
1322
1323 **/
1324 INTN
1325 MoveToNextStatement (
1326 IN BOOLEAN GoUp,
1327 IN OUT LIST_ENTRY **CurrentPosition
1328 )
1329 {
1330 INTN Distance;
1331 LIST_ENTRY *Pos;
1332 BOOLEAN HitEnd;
1333 UI_MENU_OPTION *NextMenuOption;
1334
1335 Distance = 0;
1336 Pos = *CurrentPosition;
1337 HitEnd = FALSE;
1338
1339 while (TRUE) {
1340 NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1341 if (IsSelectable (NextMenuOption)) {
1342 break;
1343 }
1344 if ((GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) {
1345 HitEnd = TRUE;
1346 break;
1347 }
1348 Distance += NextMenuOption->Skip;
1349 Pos = (GoUp ? Pos->BackLink : Pos->ForwardLink);
1350 }
1351
1352 if (HitEnd) {
1353 //
1354 // If we hit end there is still no statement can be focused,
1355 // we go backwards to find the statement can be focused.
1356 //
1357 Distance = 0;
1358 Pos = *CurrentPosition;
1359
1360 while (TRUE) {
1361 NextMenuOption = MENU_OPTION_FROM_LINK (Pos);
1362 if (IsSelectable (NextMenuOption)) {
1363 break;
1364 }
1365 if ((!GoUp ? Pos->BackLink : Pos->ForwardLink) == &Menu) {
1366 ASSERT (FALSE);
1367 break;
1368 }
1369 Distance -= NextMenuOption->Skip;
1370 Pos = (!GoUp ? Pos->BackLink : Pos->ForwardLink);
1371 }
1372 }
1373
1374 *CurrentPosition = &NextMenuOption->Link;
1375 return Distance;
1376 }
1377
1378
1379 /**
1380 Adjust Data and Time position accordingly.
1381 Data format : [01/02/2004] [11:22:33]
1382 Line number : 0 0 1 0 0 1
1383
1384 This is an internal function.
1385
1386 @param DirectionUp the up or down direction. False is down. True is
1387 up.
1388 @param CurrentPosition Current position. On return: Point to the last
1389 Option (Year or Second) if up; Point to the first
1390 Option (Month or Hour) if down.
1391
1392 @return Return line number to pad. It is possible that we stand on a zero-advance
1393 @return data or time opcode, so pad one line when we judge if we are going to scroll outside.
1394
1395 **/
1396 UINTN
1397 AdjustDateAndTimePosition (
1398 IN BOOLEAN DirectionUp,
1399 IN OUT LIST_ENTRY **CurrentPosition
1400 )
1401 {
1402 UINTN Count;
1403 LIST_ENTRY *NewPosition;
1404 UI_MENU_OPTION *MenuOption;
1405 UINTN PadLineNumber;
1406
1407 PadLineNumber = 0;
1408 NewPosition = *CurrentPosition;
1409 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1410
1411 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
1412 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
1413 //
1414 // Calculate the distance from current position to the last Date/Time MenuOption
1415 //
1416 Count = 0;
1417 while (MenuOption->Skip == 0) {
1418 Count++;
1419 NewPosition = NewPosition->ForwardLink;
1420 MenuOption = MENU_OPTION_FROM_LINK (NewPosition);
1421 PadLineNumber = 1;
1422 }
1423
1424 NewPosition = *CurrentPosition;
1425 if (DirectionUp) {
1426 //
1427 // Since the behavior of hitting the up arrow on a Date/Time MenuOption is intended
1428 // to be one that back to the previous set of MenuOptions, we need to advance to the first
1429 // Date/Time MenuOption and leave the remaining logic in CfUiUp intact so the appropriate
1430 // checking can be done.
1431 //
1432 while (Count++ < 2) {
1433 NewPosition = NewPosition->BackLink;
1434 }
1435 } else {
1436 //
1437 // Since the behavior of hitting the down arrow on a Date/Time MenuOption is intended
1438 // to be one that progresses to the next set of MenuOptions, we need to advance to the last
1439 // Date/Time MenuOption and leave the remaining logic in CfUiDown intact so the appropriate
1440 // checking can be done.
1441 //
1442 while (Count-- > 0) {
1443 NewPosition = NewPosition->ForwardLink;
1444 }
1445 }
1446
1447 *CurrentPosition = NewPosition;
1448 }
1449
1450 return PadLineNumber;
1451 }
1452
1453
1454 /**
1455 Display menu and wait for user to select one menu option, then return it.
1456 If AutoBoot is enabled, then if user doesn't select any option,
1457 after period of time, it will automatically return the first menu option.
1458
1459 @param Selection Menu selection.
1460
1461 @retval EFI_SUCESSS This function always return successfully for now.
1462
1463 **/
1464 EFI_STATUS
1465 UiDisplayMenu (
1466 IN OUT UI_MENU_SELECTION *Selection
1467 )
1468 {
1469 INTN SkipValue;
1470 INTN Difference;
1471 INTN OldSkipValue;
1472 UINTN DistanceValue;
1473 UINTN Row;
1474 UINTN Col;
1475 UINTN Temp;
1476 UINTN Temp2;
1477 UINTN TopRow;
1478 UINTN BottomRow;
1479 UINTN OriginalRow;
1480 UINTN Index;
1481 UINT32 Count;
1482 UINT16 Width;
1483 CHAR16 *StringPtr;
1484 CHAR16 *OptionString;
1485 CHAR16 *OutputString;
1486 CHAR16 *FormattedString;
1487 CHAR16 YesResponse;
1488 CHAR16 NoResponse;
1489 BOOLEAN NewLine;
1490 BOOLEAN Repaint;
1491 BOOLEAN SavedValue;
1492 EFI_STATUS Status;
1493 EFI_INPUT_KEY Key;
1494 LIST_ENTRY *Link;
1495 LIST_ENTRY *NewPos;
1496 LIST_ENTRY *TopOfScreen;
1497 LIST_ENTRY *SavedListEntry;
1498 UI_MENU_OPTION *MenuOption;
1499 UI_MENU_OPTION *NextMenuOption;
1500 UI_MENU_OPTION *SavedMenuOption;
1501 UI_MENU_OPTION *PreviousMenuOption;
1502 UI_CONTROL_FLAG ControlFlag;
1503 EFI_SCREEN_DESCRIPTOR LocalScreen;
1504 MENU_REFRESH_ENTRY *MenuRefreshEntry;
1505 UI_SCREEN_OPERATION ScreenOperation;
1506 UINT8 MinRefreshInterval;
1507 UINTN BufferSize;
1508 UINT16 DefaultId;
1509 EFI_DEVICE_PATH_PROTOCOL *DevicePath;
1510 FORM_BROWSER_STATEMENT *Statement;
1511
1512 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
1513
1514 Status = EFI_SUCCESS;
1515 FormattedString = NULL;
1516 OptionString = NULL;
1517 ScreenOperation = UiNoOperation;
1518 NewLine = TRUE;
1519 MinRefreshInterval = 0;
1520 DefaultId = 0;
1521
1522 OutputString = NULL;
1523 gUpArrow = FALSE;
1524 gDownArrow = FALSE;
1525 SkipValue = 0;
1526 OldSkipValue = 0;
1527 MenuRefreshEntry = gMenuRefreshHead;
1528
1529 NextMenuOption = NULL;
1530 PreviousMenuOption = NULL;
1531 SavedMenuOption = NULL;
1532
1533 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
1534
1535 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
1536 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1537 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1538 } else {
1539 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1540 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1541 }
1542
1543 Col = LocalScreen.LeftColumn;
1544 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
1545
1546 Selection->TopRow = TopRow;
1547 Selection->BottomRow = BottomRow;
1548 Selection->PromptCol = Col;
1549 Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1550 Selection->Statement = NULL;
1551
1552 TopOfScreen = Menu.ForwardLink;
1553 Repaint = TRUE;
1554 MenuOption = NULL;
1555
1556 //
1557 // Get user's selection
1558 //
1559 NewPos = Menu.ForwardLink;
1560
1561 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
1562 UpdateStatusBar (REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
1563
1564 ControlFlag = CfInitialization;
1565 Selection->Action = UI_ACTION_NONE;
1566 while (TRUE) {
1567 switch (ControlFlag) {
1568 case CfInitialization:
1569 if (IsListEmpty (&Menu)) {
1570 ControlFlag = CfReadKey;
1571 } else {
1572 ControlFlag = CfCheckSelection;
1573 }
1574 break;
1575
1576 case CfCheckSelection:
1577 if (Selection->Action != UI_ACTION_NONE) {
1578 ControlFlag = CfExit;
1579 } else {
1580 ControlFlag = CfRepaint;
1581 }
1582 break;
1583
1584 case CfRepaint:
1585 ControlFlag = CfRefreshHighLight;
1586
1587 if (Repaint) {
1588 //
1589 // Display menu
1590 //
1591 gDownArrow = FALSE;
1592 gUpArrow = FALSE;
1593 Row = TopRow;
1594
1595 Temp = SkipValue;
1596 Temp2 = SkipValue;
1597
1598 ClearLines (
1599 LocalScreen.LeftColumn,
1600 LocalScreen.RightColumn,
1601 TopRow - SCROLL_ARROW_HEIGHT,
1602 BottomRow + SCROLL_ARROW_HEIGHT,
1603 FIELD_TEXT | FIELD_BACKGROUND
1604 );
1605
1606 UiFreeRefreshList ();
1607 MinRefreshInterval = 0;
1608
1609 for (Link = TopOfScreen; Link != &Menu; Link = Link->ForwardLink) {
1610 MenuOption = MENU_OPTION_FROM_LINK (Link);
1611 MenuOption->Row = Row;
1612 MenuOption->Col = Col;
1613 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1614
1615 Statement = MenuOption->ThisTag;
1616 if (Statement->InSubtitle) {
1617 MenuOption->Col += SUBTITLE_INDENT;
1618 }
1619
1620 if (MenuOption->GrayOut) {
1621 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1622 } else {
1623 if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
1624 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1625 }
1626 }
1627
1628 Width = GetWidth (Statement, MenuOption->Handle);
1629 OriginalRow = Row;
1630
1631 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1632 if ((Temp == 0) && (Row <= BottomRow)) {
1633 PrintStringAt (MenuOption->Col, Row, OutputString);
1634 }
1635 //
1636 // If there is more string to process print on the next row and increment the Skip value
1637 //
1638 if (StrLen (&MenuOption->Description[Index])) {
1639 if (Temp == 0) {
1640 Row++;
1641 }
1642 }
1643
1644 gBS->FreePool (OutputString);
1645 if (Temp != 0) {
1646 Temp--;
1647 }
1648 }
1649
1650 Temp = 0;
1651 Row = OriginalRow;
1652
1653 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1654 Status = ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1655 if (EFI_ERROR (Status)) {
1656 //
1657 // Repaint to clear possible error prompt pop-up
1658 //
1659 Repaint = TRUE;
1660 NewLine = TRUE;
1661 ControlFlag = CfRepaint;
1662 break;
1663 }
1664
1665 if (OptionString != NULL) {
1666 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
1667 //
1668 // If leading spaces on OptionString - remove the spaces
1669 //
1670 for (Index = 0; OptionString[Index] == L' '; Index++) {
1671 MenuOption->OptCol++;
1672 }
1673
1674 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1675 OptionString[Count] = OptionString[Index];
1676 Count++;
1677 }
1678
1679 OptionString[Count] = CHAR_NULL;
1680 }
1681
1682 //
1683 // If Question request refresh, register the op-code
1684 //
1685 if (Statement->RefreshInterval != 0) {
1686 //
1687 // Menu will be refreshed at minimal interval of all Questions
1688 // which have refresh request
1689 //
1690 if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
1691 MinRefreshInterval = Statement->RefreshInterval;
1692 }
1693
1694 if (gMenuRefreshHead == NULL) {
1695 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1696 ASSERT (MenuRefreshEntry != NULL);
1697 MenuRefreshEntry->MenuOption = MenuOption;
1698 MenuRefreshEntry->Selection = Selection;
1699 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1700 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1701 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1702 gMenuRefreshHead = MenuRefreshEntry;
1703 } else {
1704 //
1705 // Advance to the last entry
1706 //
1707 for (MenuRefreshEntry = gMenuRefreshHead;
1708 MenuRefreshEntry->Next != NULL;
1709 MenuRefreshEntry = MenuRefreshEntry->Next
1710 )
1711 ;
1712 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1713 ASSERT (MenuRefreshEntry->Next != NULL);
1714 MenuRefreshEntry = MenuRefreshEntry->Next;
1715 MenuRefreshEntry->MenuOption = MenuOption;
1716 MenuRefreshEntry->Selection = Selection;
1717 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1718 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1719 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
1720 }
1721 }
1722
1723 Width = (UINT16) gOptionBlockWidth;
1724 OriginalRow = Row;
1725
1726 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1727 if ((Temp2 == 0) && (Row <= BottomRow)) {
1728 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1729 }
1730 //
1731 // If there is more string to process print on the next row and increment the Skip value
1732 //
1733 if (StrLen (&OptionString[Index])) {
1734 if (Temp2 == 0) {
1735 Row++;
1736 //
1737 // Since the Number of lines for this menu entry may or may not be reflected accurately
1738 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1739 // some testing to ensure we are keeping this in-sync.
1740 //
1741 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1742 //
1743 if ((Row - OriginalRow) >= MenuOption->Skip) {
1744 MenuOption->Skip++;
1745 }
1746 }
1747 }
1748
1749 gBS->FreePool (OutputString);
1750 if (Temp2 != 0) {
1751 Temp2--;
1752 }
1753 }
1754
1755 Temp2 = 0;
1756 Row = OriginalRow;
1757
1758 gBS->FreePool (OptionString);
1759 }
1760 //
1761 // If this is a text op with secondary text information
1762 //
1763 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1764 StringPtr = GetToken (Statement->TextTwo, MenuOption->Handle);
1765
1766 Width = (UINT16) gOptionBlockWidth;
1767 OriginalRow = Row;
1768
1769 for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
1770 if ((Temp == 0) && (Row <= BottomRow)) {
1771 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1772 }
1773 //
1774 // If there is more string to process print on the next row and increment the Skip value
1775 //
1776 if (StrLen (&StringPtr[Index])) {
1777 if (Temp2 == 0) {
1778 Row++;
1779 //
1780 // Since the Number of lines for this menu entry may or may not be reflected accurately
1781 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1782 // some testing to ensure we are keeping this in-sync.
1783 //
1784 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1785 //
1786 if ((Row - OriginalRow) >= MenuOption->Skip) {
1787 MenuOption->Skip++;
1788 }
1789 }
1790 }
1791
1792 gBS->FreePool (OutputString);
1793 if (Temp2 != 0) {
1794 Temp2--;
1795 }
1796 }
1797
1798 Row = OriginalRow;
1799 gBS->FreePool (StringPtr);
1800 }
1801
1802 //
1803 // Need to handle the bottom of the display
1804 //
1805 if (MenuOption->Skip > 1) {
1806 Row += MenuOption->Skip - SkipValue;
1807 SkipValue = 0;
1808 } else {
1809 Row += MenuOption->Skip;
1810 }
1811
1812 if (Row > BottomRow) {
1813 if (!ValueIsScroll (FALSE, Link)) {
1814 gDownArrow = TRUE;
1815 }
1816
1817 Row = BottomRow + 1;
1818 break;
1819 }
1820 }
1821
1822 if (!ValueIsScroll (TRUE, TopOfScreen)) {
1823 gUpArrow = TRUE;
1824 }
1825
1826 if (gUpArrow) {
1827 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1828 PrintAt (
1829 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1830 TopRow - SCROLL_ARROW_HEIGHT,
1831 L"%c",
1832 ARROW_UP
1833 );
1834 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1835 }
1836
1837 if (gDownArrow) {
1838 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
1839 PrintAt (
1840 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
1841 BottomRow + SCROLL_ARROW_HEIGHT,
1842 L"%c",
1843 ARROW_DOWN
1844 );
1845 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1846 }
1847
1848 MenuOption = NULL;
1849 }
1850 break;
1851
1852 case CfRefreshHighLight:
1853 //
1854 // MenuOption: Last menu option that need to remove hilight
1855 // MenuOption is set to NULL in Repaint
1856 // NewPos: Current menu option that need to hilight
1857 //
1858 ControlFlag = CfUpdateHelpString;
1859
1860 //
1861 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
1862 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
1863 //
1864 SavedValue = Repaint;
1865 Repaint = FALSE;
1866
1867 if (Selection->QuestionId != 0) {
1868 NewPos = Menu.ForwardLink;
1869 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
1870
1871 while (SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId && NewPos->ForwardLink != &Menu) {
1872 NewPos = NewPos->ForwardLink;
1873 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
1874 }
1875 if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
1876 //
1877 // Target Question found, find its MenuOption
1878 //
1879 Link = TopOfScreen;
1880
1881 for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
1882 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
1883 Index += SavedMenuOption->Skip;
1884 Link = Link->ForwardLink;
1885 }
1886
1887 if (Link != NewPos || Index > BottomRow) {
1888 //
1889 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
1890 //
1891 Link = NewPos;
1892 for (Index = TopRow; Index <= BottomRow; ) {
1893 Link = Link->BackLink;
1894 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
1895 Index += SavedMenuOption->Skip;
1896 }
1897 TopOfScreen = Link->ForwardLink;
1898
1899 Repaint = TRUE;
1900 NewLine = TRUE;
1901 ControlFlag = CfRepaint;
1902 break;
1903 }
1904 } else {
1905 //
1906 // Target Question not found, highlight the default menu option
1907 //
1908 NewPos = TopOfScreen;
1909 }
1910
1911 Selection->QuestionId = 0;
1912 }
1913
1914 if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
1915 if (MenuOption != NULL) {
1916 //
1917 // Remove highlight on last Menu Option
1918 //
1919 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
1920 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1921 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1922 if (OptionString != NULL) {
1923 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
1924 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
1925 ) {
1926 //
1927 // If leading spaces on OptionString - remove the spaces
1928 //
1929 for (Index = 0; OptionString[Index] == L' '; Index++)
1930 ;
1931
1932 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1933 OptionString[Count] = OptionString[Index];
1934 Count++;
1935 }
1936
1937 OptionString[Count] = CHAR_NULL;
1938 }
1939
1940 Width = (UINT16) gOptionBlockWidth;
1941 OriginalRow = MenuOption->Row;
1942
1943 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1944 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1945 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
1946 }
1947 //
1948 // If there is more string to process print on the next row and increment the Skip value
1949 //
1950 if (StrLen (&OptionString[Index])) {
1951 MenuOption->Row++;
1952 }
1953
1954 gBS->FreePool (OutputString);
1955 }
1956
1957 MenuOption->Row = OriginalRow;
1958
1959 gBS->FreePool (OptionString);
1960 } else {
1961 if (NewLine) {
1962 if (MenuOption->GrayOut) {
1963 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1964 } else if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
1965 gST->ConOut->SetAttribute (gST->ConOut, SUBTITLE_TEXT | FIELD_BACKGROUND);
1966 }
1967
1968 OriginalRow = MenuOption->Row;
1969 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
1970
1971 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1972 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
1973 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
1974 }
1975 //
1976 // If there is more string to process print on the next row and increment the Skip value
1977 //
1978 if (StrLen (&MenuOption->Description[Index])) {
1979 MenuOption->Row++;
1980 }
1981
1982 gBS->FreePool (OutputString);
1983 }
1984
1985 MenuOption->Row = OriginalRow;
1986 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
1987 }
1988 }
1989 }
1990
1991 //
1992 // This is only possible if we entered this page and the first menu option is
1993 // a "non-menu" item. In that case, force it UiDown
1994 //
1995 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
1996 if (!IsSelectable (MenuOption)) {
1997 ASSERT (ScreenOperation == UiNoOperation);
1998 ScreenOperation = UiDown;
1999 ControlFlag = CfScreenOperation;
2000 break;
2001 }
2002
2003 //
2004 // This is the current selected statement
2005 //
2006 Statement = MenuOption->ThisTag;
2007 Selection->Statement = Statement;
2008
2009 //
2010 // Set reverse attribute
2011 //
2012 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT);
2013 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2014
2015 //
2016 // Assuming that we have a refresh linked-list created, lets annotate the
2017 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2018 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2019 //
2020 if (gMenuRefreshHead != NULL) {
2021 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
2022 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT | FIELD_BACKGROUND;
2023 if (MenuRefreshEntry->MenuOption == MenuOption) {
2024 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_HIGHLIGHT | FIELD_BACKGROUND_HIGHLIGHT;
2025 }
2026 }
2027 }
2028
2029 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2030 if (OptionString != NULL) {
2031 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
2032 //
2033 // If leading spaces on OptionString - remove the spaces
2034 //
2035 for (Index = 0; OptionString[Index] == L' '; Index++)
2036 ;
2037
2038 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
2039 OptionString[Count] = OptionString[Index];
2040 Count++;
2041 }
2042
2043 OptionString[Count] = CHAR_NULL;
2044 }
2045 Width = (UINT16) gOptionBlockWidth;
2046
2047 OriginalRow = MenuOption->Row;
2048
2049 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
2050 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2051 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2052 }
2053 //
2054 // If there is more string to process print on the next row and increment the Skip value
2055 //
2056 if (StrLen (&OptionString[Index])) {
2057 MenuOption->Row++;
2058 }
2059
2060 gBS->FreePool (OutputString);
2061 }
2062
2063 MenuOption->Row = OriginalRow;
2064
2065 gBS->FreePool (OptionString);
2066 } else {
2067 if (NewLine) {
2068 OriginalRow = MenuOption->Row;
2069
2070 Width = GetWidth (Statement, MenuOption->Handle);
2071
2072 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
2073 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2074 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2075 }
2076 //
2077 // If there is more string to process print on the next row and increment the Skip value
2078 //
2079 if (StrLen (&MenuOption->Description[Index])) {
2080 MenuOption->Row++;
2081 }
2082
2083 gBS->FreePool (OutputString);
2084 }
2085
2086 MenuOption->Row = OriginalRow;
2087
2088 }
2089 }
2090
2091 UpdateKeyHelp (MenuOption, FALSE);
2092
2093 //
2094 // Clear reverse attribute
2095 //
2096 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT | FIELD_BACKGROUND);
2097 }
2098 //
2099 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2100 // if we didn't break halfway when process CfRefreshHighLight.
2101 //
2102 Repaint = SavedValue;
2103 break;
2104
2105 case CfUpdateHelpString:
2106 ControlFlag = CfPrepareToReadKey;
2107
2108 if ((Repaint || NewLine) && (gClassOfVfr != EFI_GENERAL_APPLICATION_SUBCLASS)) {
2109 //
2110 // Don't print anything if it is a NULL help token
2111 //
2112 if (MenuOption->ThisTag->Help == 0) {
2113 StringPtr = L"\0";
2114 } else {
2115 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2116 }
2117
2118 ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
2119
2120 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
2121
2122 for (Index = 0; Index < BottomRow - TopRow; Index++) {
2123 //
2124 // Pad String with spaces to simulate a clearing of the previous line
2125 //
2126 for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth * 2]) / 2 < gHelpBlockWidth;) {
2127 StrCat (&FormattedString[Index * gHelpBlockWidth * 2], L" ");
2128 }
2129
2130 PrintStringAt (
2131 LocalScreen.RightColumn - gHelpBlockWidth,
2132 Index + TopRow,
2133 &FormattedString[Index * gHelpBlockWidth * 2]
2134 );
2135 }
2136 }
2137 //
2138 // Reset this flag every time we finish using it.
2139 //
2140 Repaint = FALSE;
2141 NewLine = FALSE;
2142 break;
2143
2144 case CfPrepareToReadKey:
2145 ControlFlag = CfReadKey;
2146 ScreenOperation = UiNoOperation;
2147 break;
2148
2149 case CfReadKey:
2150 ControlFlag = CfScreenOperation;
2151
2152 //
2153 // Wait for user's selection
2154 //
2155 do {
2156 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
2157 } while (Status == EFI_TIMEOUT);
2158
2159 if (Selection->Action == UI_ACTION_REFRESH_FORMSET) {
2160 //
2161 // IFR is updated in Callback of refresh opcode, re-parse it
2162 //
2163 ControlFlag = CfUiReset;
2164 Selection->Statement = NULL;
2165 break;
2166 }
2167
2168 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2169 //
2170 // if we encounter error, continue to read another key in.
2171 //
2172 if (EFI_ERROR (Status)) {
2173 ControlFlag = CfReadKey;
2174 break;
2175 }
2176
2177 if (IsListEmpty (&Menu) && Key.UnicodeChar != CHAR_NULL) {
2178 //
2179 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2180 //
2181 break;
2182 }
2183
2184 switch (Key.UnicodeChar) {
2185 case CHAR_CARRIAGE_RETURN:
2186 ScreenOperation = UiSelect;
2187 gDirection = 0;
2188 break;
2189
2190 //
2191 // We will push the adjustment of these numeric values directly to the input handler
2192 // NOTE: we won't handle manual input numeric
2193 //
2194 case '+':
2195 case '-':
2196 Statement = MenuOption->ThisTag;
2197 if ((Statement->Operand == EFI_IFR_DATE_OP)
2198 || (Statement->Operand == EFI_IFR_TIME_OP)
2199 || ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
2200 ){
2201 if (Key.UnicodeChar == '+') {
2202 gDirection = SCAN_RIGHT;
2203 } else {
2204 gDirection = SCAN_LEFT;
2205 }
2206 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2207 if (EFI_ERROR (Status)) {
2208 //
2209 // Repaint to clear possible error prompt pop-up
2210 //
2211 Repaint = TRUE;
2212 NewLine = TRUE;
2213 }
2214 if (OptionString != NULL) {
2215 FreePool (OptionString);
2216 }
2217 }
2218 break;
2219
2220 case '^':
2221 ScreenOperation = UiUp;
2222 break;
2223
2224 case 'V':
2225 case 'v':
2226 ScreenOperation = UiDown;
2227 break;
2228
2229 case ' ':
2230 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
2231 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) {
2232 ScreenOperation = UiSelect;
2233 }
2234 }
2235 break;
2236
2237 case CHAR_NULL:
2238 if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
2239 ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
2240 ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2241 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2242 ) {
2243 //
2244 // If the function key has been disabled, just ignore the key.
2245 //
2246 } else {
2247 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2248 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2249 if (Key.ScanCode == SCAN_F9) {
2250 //
2251 // Reset to standard default
2252 //
2253 DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD;
2254 }
2255 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2256 break;
2257 }
2258 }
2259 }
2260 break;
2261 }
2262 break;
2263
2264 case CfScreenOperation:
2265 if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
2266 //
2267 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2268 // ignore the selection and go back to reading keys.
2269 //
2270 if (IsListEmpty (&Menu)) {
2271 ControlFlag = CfReadKey;
2272 break;
2273 }
2274 //
2275 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2276 //
2277 for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
2278 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2279 if (IsSelectable (NextMenuOption)) {
2280 break;
2281 }
2282 }
2283
2284 if (Link == &Menu) {
2285 ControlFlag = CfPrepareToReadKey;
2286 break;
2287 }
2288 } else if (ScreenOperation == UiReset) {
2289 //
2290 // Press ESC to exit FormSet
2291 //
2292 Selection->Action = UI_ACTION_EXIT;
2293 Selection->Statement = NULL;
2294 }
2295
2296 for (Index = 0;
2297 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2298 Index++
2299 ) {
2300 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2301 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2302 break;
2303 }
2304 }
2305 break;
2306
2307 case CfUiPrevious:
2308 ControlFlag = CfCheckSelection;
2309
2310 if (IsListEmpty (&gMenuList)) {
2311 Selection->Action = UI_ACTION_NONE;
2312 if (IsListEmpty (&Menu)) {
2313 ControlFlag = CfReadKey;
2314 }
2315 break;
2316 }
2317
2318 //
2319 // Remove the Cached page entry
2320 //
2321 UiRemoveMenuListEntry (Selection);
2322
2323 Selection->Action = UI_ACTION_REFRESH_FORM;
2324 Selection->Statement = NULL;
2325 break;
2326
2327 case CfUiSelect:
2328 ControlFlag = CfCheckSelection;
2329
2330 Statement = MenuOption->ThisTag;
2331 if ((Statement->Operand == EFI_IFR_TEXT_OP) ||
2332 (Statement->Operand == EFI_IFR_DATE_OP) ||
2333 (Statement->Operand == EFI_IFR_TIME_OP) ||
2334 (Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0)) {
2335 break;
2336 }
2337
2338 //
2339 // Keep highlight on current MenuOption
2340 //
2341 Selection->QuestionId = Statement->QuestionId;
2342
2343 switch (Statement->Operand) {
2344 case EFI_IFR_REF_OP:
2345 if (Statement->RefDevicePath != 0) {
2346 //
2347 // Goto another Hii Package list
2348 //
2349 ControlFlag = CfUiReset;
2350 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2351
2352 StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle);
2353 if (StringPtr == NULL) {
2354 //
2355 // No device path string not found, exit
2356 //
2357 Selection->Action = UI_ACTION_EXIT;
2358 Selection->Statement = NULL;
2359 break;
2360 }
2361 BufferSize = StrLen (StringPtr) / 2;
2362 DevicePath = AllocatePool (BufferSize);
2363
2364 HexStringToBufInReverseOrder ((UINT8 *) DevicePath, &BufferSize, StringPtr);
2365 Selection->Handle = HiiLibDevicePathToHiiHandle (DevicePath);
2366 if (Selection->Handle == NULL) {
2367 //
2368 // If target Hii Handle not found, exit
2369 //
2370 Selection->Action = UI_ACTION_EXIT;
2371 Selection->Statement = NULL;
2372 break;
2373 }
2374
2375 gBS->FreePool (StringPtr);
2376 gBS->FreePool (DevicePath);
2377
2378 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2379 Selection->FormId = Statement->RefFormId;
2380 Selection->QuestionId = Statement->RefQuestionId;
2381 } else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) {
2382 //
2383 // Goto another Formset, check for uncommitted data
2384 //
2385 ControlFlag = CfUiReset;
2386 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2387
2388 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2389 Selection->FormId = Statement->RefFormId;
2390 Selection->QuestionId = Statement->RefQuestionId;
2391 } else if (Statement->RefFormId != 0) {
2392 //
2393 // Goto another form inside this formset,
2394 //
2395 Selection->Action = UI_ACTION_REFRESH_FORM;
2396
2397 //
2398 // Link current form so that we can always go back when someone hits the UiPrevious
2399 //
2400 UiAddMenuListEntry (Selection);
2401
2402 Selection->FormId = Statement->RefFormId;
2403 Selection->QuestionId = Statement->RefQuestionId;
2404 } else if (Statement->RefQuestionId != 0) {
2405 //
2406 // Goto another Question
2407 //
2408 Selection->QuestionId = Statement->RefQuestionId;
2409
2410 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2411 Selection->Action = UI_ACTION_REFRESH_FORM;
2412 } else {
2413 Repaint = TRUE;
2414 NewLine = TRUE;
2415 break;
2416 }
2417 }
2418 break;
2419
2420 case EFI_IFR_ACTION_OP:
2421 //
2422 // Process the Config string <ConfigResp>
2423 //
2424 Status = ProcessQuestionConfig (Selection, Statement);
2425
2426 if (EFI_ERROR (Status)) {
2427 break;
2428 }
2429
2430 //
2431 // The action button may change some Question value, so refresh the form
2432 //
2433 Selection->Action = UI_ACTION_REFRESH_FORM;
2434 break;
2435
2436 case EFI_IFR_RESET_BUTTON_OP:
2437 //
2438 // Reset Question to default value specified by DefaultId
2439 //
2440 ControlFlag = CfUiDefault;
2441 DefaultId = Statement->DefaultId;
2442 break;
2443
2444 default:
2445 //
2446 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2447 //
2448 UpdateKeyHelp (MenuOption, TRUE);
2449 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2450
2451 if (EFI_ERROR (Status)) {
2452 Repaint = TRUE;
2453 NewLine = TRUE;
2454 UpdateKeyHelp (MenuOption, FALSE);
2455 } else {
2456 Selection->Action = UI_ACTION_REFRESH_FORM;
2457 }
2458
2459 if (OptionString != NULL) {
2460 FreePool (OptionString);
2461 }
2462 break;
2463 }
2464 break;
2465
2466 case CfUiReset:
2467 //
2468 // We are going to leave current FormSet, so check uncommited data in this FormSet
2469 //
2470 ControlFlag = CfCheckSelection;
2471
2472 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
2473 //
2474 // There is no parent menu for FrontPage
2475 //
2476 Selection->Action = UI_ACTION_NONE;
2477 Selection->Statement = MenuOption->ThisTag;
2478 break;
2479 }
2480
2481 //
2482 // If NV flag is up, prompt user
2483 //
2484 if (gNvUpdateRequired) {
2485 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2486
2487 YesResponse = gYesResponse[0];
2488 NoResponse = gNoResponse[0];
2489
2490 do {
2491 CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
2492 } while
2493 (
2494 (Key.ScanCode != SCAN_ESC) &&
2495 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
2496 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
2497 );
2498
2499 //
2500 // If the user hits the YesResponse key
2501 //
2502 if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
2503 } else {
2504 Repaint = TRUE;
2505 NewLine = TRUE;
2506
2507 Selection->Action = UI_ACTION_NONE;
2508 break;
2509 }
2510 }
2511
2512 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2513 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2514
2515 UiFreeMenuList ();
2516 gST->ConOut->ClearScreen (gST->ConOut);
2517 return EFI_SUCCESS;
2518
2519 case CfUiLeft:
2520 ControlFlag = CfCheckSelection;
2521 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2522 if (MenuOption->Sequence != 0) {
2523 //
2524 // In the middle or tail of the Date/Time op-code set, go left.
2525 //
2526 NewPos = NewPos->BackLink;
2527 }
2528 }
2529 break;
2530
2531 case CfUiRight:
2532 ControlFlag = CfCheckSelection;
2533 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2534 if (MenuOption->Sequence != 2) {
2535 //
2536 // In the middle or tail of the Date/Time op-code set, go left.
2537 //
2538 NewPos = NewPos->ForwardLink;
2539 }
2540 }
2541 break;
2542
2543 case CfUiUp:
2544 ControlFlag = CfCheckSelection;
2545
2546 SavedListEntry = TopOfScreen;
2547
2548 if (NewPos->BackLink != &Menu) {
2549 NewLine = TRUE;
2550 //
2551 // Adjust Date/Time position before we advance forward.
2552 //
2553 AdjustDateAndTimePosition (TRUE, &NewPos);
2554
2555 //
2556 // Caution that we have already rewind to the top, don't go backward in this situation.
2557 //
2558 if (NewPos->BackLink != &Menu) {
2559 NewPos = NewPos->BackLink;
2560 }
2561
2562 PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2563 DistanceValue = PreviousMenuOption->Skip;
2564
2565 //
2566 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2567 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2568 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2569 // checking can be done.
2570 //
2571 DistanceValue += AdjustDateAndTimePosition (TRUE, &NewPos);
2572
2573 //
2574 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2575 // don't worry about a redraw.
2576 //
2577 if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
2578 Repaint = TRUE;
2579 TopOfScreen = NewPos;
2580 }
2581
2582 Difference = MoveToNextStatement (TRUE, &NewPos);
2583 if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
2584 if (Difference > 0) {
2585 //
2586 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2587 //
2588 TopOfScreen = NewPos;
2589 Repaint = TRUE;
2590 }
2591 }
2592 if (Difference < 0) {
2593 //
2594 // We want to goto previous MenuOption, but finally we go down.
2595 // it means that we hit the begining MenuOption that can be focused
2596 // so we simply scroll to the top
2597 //
2598 if (SavedListEntry != Menu.ForwardLink) {
2599 TopOfScreen = Menu.ForwardLink;
2600 Repaint = TRUE;
2601 }
2602 }
2603
2604 //
2605 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2606 //
2607 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2608
2609 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2610 } else {
2611 SavedMenuOption = MenuOption;
2612 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2613 if (!IsSelectable (MenuOption)) {
2614 //
2615 // If we are at the end of the list and sitting on a text op, we need to more forward
2616 //
2617 ScreenOperation = UiDown;
2618 ControlFlag = CfScreenOperation;
2619 break;
2620 }
2621
2622 MenuOption = SavedMenuOption;
2623 }
2624 break;
2625
2626 case CfUiPageUp:
2627 ControlFlag = CfCheckSelection;
2628
2629 if (NewPos->BackLink == &Menu) {
2630 NewLine = FALSE;
2631 Repaint = FALSE;
2632 break;
2633 }
2634
2635 NewLine = TRUE;
2636 Repaint = TRUE;
2637 Link = TopOfScreen;
2638 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2639 Index = BottomRow;
2640 while ((Index >= TopRow) && (Link->BackLink != &Menu)) {
2641 Index = Index - PreviousMenuOption->Skip;
2642 Link = Link->BackLink;
2643 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2644 }
2645
2646 TopOfScreen = Link;
2647 Difference = MoveToNextStatement (TRUE, &Link);
2648 if (Difference > 0) {
2649 //
2650 // The focus MenuOption is above the TopOfScreen
2651 //
2652 TopOfScreen = Link;
2653 } else if (Difference < 0) {
2654 //
2655 // This happens when there is no MenuOption can be focused from
2656 // Current MenuOption to the first MenuOption
2657 //
2658 TopOfScreen = Menu.ForwardLink;
2659 }
2660 Index += Difference;
2661 if (Index < TopRow) {
2662 MenuOption = NULL;
2663 }
2664
2665 if (NewPos == Link) {
2666 Repaint = FALSE;
2667 NewLine = FALSE;
2668 } else {
2669 NewPos = Link;
2670 }
2671
2672 //
2673 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2674 // Don't do this when we are already in the first page.
2675 //
2676 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2677 AdjustDateAndTimePosition (TRUE, &NewPos);
2678 break;
2679
2680 case CfUiPageDown:
2681 ControlFlag = CfCheckSelection;
2682
2683 if (NewPos->ForwardLink == &Menu) {
2684 NewLine = FALSE;
2685 Repaint = FALSE;
2686 break;
2687 }
2688
2689 NewLine = TRUE;
2690 Repaint = TRUE;
2691 Link = TopOfScreen;
2692 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2693 Index = TopRow;
2694 while ((Index <= BottomRow) && (Link->ForwardLink != &Menu)) {
2695 Index = Index + NextMenuOption->Skip;
2696 Link = Link->ForwardLink;
2697 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2698 }
2699
2700 Index += MoveToNextStatement (FALSE, &Link);
2701 if (Index > BottomRow) {
2702 //
2703 // There are more MenuOption needing scrolling
2704 //
2705 TopOfScreen = Link;
2706 MenuOption = NULL;
2707 }
2708 if (NewPos == Link && Index <= BottomRow) {
2709 //
2710 // Finally we know that NewPos is the last MenuOption can be focused.
2711 //
2712 NewLine = FALSE;
2713 Repaint = FALSE;
2714 } else {
2715 NewPos = Link;
2716 }
2717
2718 //
2719 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2720 // Don't do this when we are already in the last page.
2721 //
2722 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2723 AdjustDateAndTimePosition (TRUE, &NewPos);
2724 break;
2725
2726 case CfUiDown:
2727 ControlFlag = CfCheckSelection;
2728 //
2729 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2730 // to be one that progresses to the next set of op-codes, we need to advance to the last
2731 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2732 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2733 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2734 // the Date/Time op-code.
2735 //
2736 SavedListEntry = NewPos;
2737 DistanceValue = AdjustDateAndTimePosition (FALSE, &NewPos);
2738
2739 if (NewPos->ForwardLink != &Menu) {
2740 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2741 NewLine = TRUE;
2742 NewPos = NewPos->ForwardLink;
2743 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2744
2745 DistanceValue += NextMenuOption->Skip;
2746 DistanceValue += MoveToNextStatement (FALSE, &NewPos);
2747 //
2748 // An option might be multi-line, so we need to reflect that data in the overall skip value
2749 //
2750 UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, SkipValue);
2751
2752 Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
2753 if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
2754 (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
2755 NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
2756 ) {
2757 Temp ++;
2758 }
2759
2760 //
2761 // If we are going to scroll, update TopOfScreen
2762 //
2763 if (Temp > BottomRow) {
2764 do {
2765 //
2766 // Is the current top of screen a zero-advance op-code?
2767 // If so, keep moving forward till we hit a >0 advance op-code
2768 //
2769 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2770
2771 //
2772 // If bottom op-code is more than one line or top op-code is more than one line
2773 //
2774 if ((DistanceValue > 1) || (MenuOption->Skip > 1)) {
2775 //
2776 // Is the bottom op-code greater than or equal in size to the top op-code?
2777 //
2778 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
2779 //
2780 // Skip the top op-code
2781 //
2782 TopOfScreen = TopOfScreen->ForwardLink;
2783 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
2784
2785 OldSkipValue = Difference;
2786
2787 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2788
2789 //
2790 // If we have a remainder, skip that many more op-codes until we drain the remainder
2791 //
2792 for (;
2793 Difference >= (INTN) SavedMenuOption->Skip;
2794 Difference = Difference - (INTN) SavedMenuOption->Skip
2795 ) {
2796 //
2797 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2798 //
2799 TopOfScreen = TopOfScreen->ForwardLink;
2800 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2801 if (Difference < (INTN) SavedMenuOption->Skip) {
2802 Difference = SavedMenuOption->Skip - Difference - 1;
2803 break;
2804 } else {
2805 if (Difference == (INTN) SavedMenuOption->Skip) {
2806 TopOfScreen = TopOfScreen->ForwardLink;
2807 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2808 Difference = SavedMenuOption->Skip - Difference;
2809 break;
2810 }
2811 }
2812 }
2813 //
2814 // Since we will act on this op-code in the next routine, and increment the
2815 // SkipValue, set the skips to one less than what is required.
2816 //
2817 SkipValue = Difference - 1;
2818
2819 } else {
2820 //
2821 // Since we will act on this op-code in the next routine, and increment the
2822 // SkipValue, set the skips to one less than what is required.
2823 //
2824 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
2825 }
2826 } else {
2827 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
2828 TopOfScreen = TopOfScreen->ForwardLink;
2829 break;
2830 } else {
2831 SkipValue = OldSkipValue;
2832 }
2833 }
2834 //
2835 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2836 // Let's set a skip flag to smoothly scroll the top of the screen.
2837 //
2838 if (SavedMenuOption->Skip > 1) {
2839 if (SavedMenuOption == NextMenuOption) {
2840 SkipValue = 0;
2841 } else {
2842 SkipValue++;
2843 }
2844 } else {
2845 SkipValue = 0;
2846 TopOfScreen = TopOfScreen->ForwardLink;
2847 }
2848 } while (SavedMenuOption->Skip == 0);
2849
2850 Repaint = TRUE;
2851 OldSkipValue = SkipValue;
2852 }
2853
2854 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2855
2856 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2857
2858 } else {
2859 SavedMenuOption = MenuOption;
2860 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2861 if (!IsSelectable (MenuOption)) {
2862 //
2863 // If we are at the end of the list and sitting on a text op, we need to more forward
2864 //
2865 ScreenOperation = UiUp;
2866 ControlFlag = CfScreenOperation;
2867 break;
2868 }
2869
2870 MenuOption = SavedMenuOption;
2871 //
2872 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2873 //
2874 AdjustDateAndTimePosition (TRUE, &NewPos);
2875 }
2876 break;
2877
2878 case CfUiSave:
2879 ControlFlag = CfCheckSelection;
2880
2881 //
2882 // Submit the form
2883 //
2884 Status = SubmitForm (Selection->FormSet, Selection->Form);
2885
2886 if (!EFI_ERROR (Status)) {
2887 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2888 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
2889 } else {
2890 do {
2891 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString);
2892 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2893
2894 Repaint = TRUE;
2895 NewLine = TRUE;
2896 }
2897 break;
2898
2899 case CfUiDefault:
2900 ControlFlag = CfCheckSelection;
2901
2902 Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId);
2903
2904 if (!EFI_ERROR (Status)) {
2905 Selection->Action = UI_ACTION_REFRESH_FORM;
2906 Selection->Statement = NULL;
2907
2908 //
2909 // Show NV update flag on status bar
2910 //
2911 gNvUpdateRequired = TRUE;
2912 }
2913 break;
2914
2915 case CfUiNoOperation:
2916 ControlFlag = CfCheckSelection;
2917 break;
2918
2919 case CfExit:
2920 UiFreeRefreshList ();
2921
2922 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2923 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
2924 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2925 gST->ConOut->OutputString (gST->ConOut, L"\n");
2926
2927 return EFI_SUCCESS;
2928
2929 default:
2930 break;
2931 }
2932 }
2933 }