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