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