]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
Change functional static variable to Global variable. No STATIC modifier is recommend...
[mirror_edk2.git] / MdeModulePkg / Universal / SetupBrowserDxe / Ui.c
1 /** @file
2 Utility functions for User Interface functions.
3
4 Copyright (c) 2004 - 2008, Intel Corporation
5 All rights reserved. This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "Ui.h"
16 #include "Setup.h"
17
18 LIST_ENTRY Menu;
19 LIST_ENTRY gMenuList;
20 MENU_REFRESH_ENTRY *gMenuRefreshHead;
21
22 //
23 // Search table for UiDisplayMenu()
24 //
25 SCAN_CODE_TO_SCREEN_OPERATION gScanCodeToOperation[] = {
26 {
27 SCAN_UP,
28 UiUp,
29 },
30 {
31 SCAN_DOWN,
32 UiDown,
33 },
34 {
35 SCAN_PAGE_UP,
36 UiPageUp,
37 },
38 {
39 SCAN_PAGE_DOWN,
40 UiPageDown,
41 },
42 {
43 SCAN_ESC,
44 UiReset,
45 },
46 {
47 SCAN_F2,
48 UiPrevious,
49 },
50 {
51 SCAN_LEFT,
52 UiLeft,
53 },
54 {
55 SCAN_RIGHT,
56 UiRight,
57 },
58 {
59 SCAN_F9,
60 UiDefault,
61 },
62 {
63 SCAN_F10,
64 UiSave
65 }
66 };
67
68 SCREEN_OPERATION_T0_CONTROL_FLAG gScreenOperationToControlFlag[] = {
69 {
70 UiNoOperation,
71 CfUiNoOperation,
72 },
73 {
74 UiDefault,
75 CfUiDefault,
76 },
77 {
78 UiSelect,
79 CfUiSelect,
80 },
81 {
82 UiUp,
83 CfUiUp,
84 },
85 {
86 UiDown,
87 CfUiDown,
88 },
89 {
90 UiLeft,
91 CfUiLeft,
92 },
93 {
94 UiRight,
95 CfUiRight,
96 },
97 {
98 UiReset,
99 CfUiReset,
100 },
101 {
102 UiSave,
103 CfUiSave,
104 },
105 {
106 UiPrevious,
107 CfUiPrevious,
108 },
109 {
110 UiPageUp,
111 CfUiPageUp,
112 },
113 {
114 UiPageDown,
115 CfUiPageDown
116 }
117 };
118
119 BOOLEAN mInputError;
120
121
122 /**
123 Set Buffer to Value for Size bytes.
124
125 @param Buffer Memory to set.
126 @param Size Number of bytes to set
127 @param Value Value of the set operation.
128
129 @return Value.
130
131 **/
132 VOID
133 SetUnicodeMem (
134 IN VOID *Buffer,
135 IN UINTN Size,
136 IN CHAR16 Value
137 )
138 {
139 CHAR16 *Ptr;
140
141 Ptr = Buffer;
142 while ((Size--) != 0) {
143 *(Ptr++) = Value;
144 }
145 }
146
147
148 /**
149 Initialize Menu option list.
150
151 **/
152 VOID
153 UiInitMenu (
154 VOID
155 )
156 {
157 InitializeListHead (&Menu);
158 }
159
160
161 /**
162 Initialize Menu option list.
163
164 **/
165 VOID
166 UiInitMenuList (
167 VOID
168 )
169 {
170 InitializeListHead (&gMenuList);
171 }
172
173
174 /**
175 Remove a Menu in list, and return FormId/QuestionId for previous Menu.
176
177 @param Selection Menu selection.
178
179 **/
180 VOID
181 UiRemoveMenuListEntry (
182 OUT UI_MENU_SELECTION *Selection
183 )
184 {
185 UI_MENU_LIST *UiMenuList;
186
187 if (!IsListEmpty (&gMenuList)) {
188 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
189
190 Selection->FormId = UiMenuList->FormId;
191 Selection->QuestionId = UiMenuList->QuestionId;
192 RemoveEntryList (&UiMenuList->MenuLink);
193 gBS->FreePool (UiMenuList);
194 }
195 }
196
197
198 /**
199 Free Menu option linked list.
200
201 **/
202 VOID
203 UiFreeMenuList (
204 VOID
205 )
206 {
207 UI_MENU_LIST *UiMenuList;
208
209 while (!IsListEmpty (&gMenuList)) {
210 UiMenuList = CR (gMenuList.ForwardLink, UI_MENU_LIST, MenuLink, UI_MENU_LIST_SIGNATURE);
211 RemoveEntryList (&UiMenuList->MenuLink);
212 gBS->FreePool (UiMenuList);
213 }
214 }
215
216
217 /**
218 Add one menu entry to the linked lst
219
220 @param Selection Menu selection.
221
222 **/
223 VOID
224 UiAddMenuListEntry (
225 IN UI_MENU_SELECTION *Selection
226 )
227 {
228 UI_MENU_LIST *UiMenuList;
229
230 UiMenuList = AllocateZeroPool (sizeof (UI_MENU_LIST));
231 ASSERT (UiMenuList != NULL);
232
233 UiMenuList->Signature = UI_MENU_LIST_SIGNATURE;
234 UiMenuList->FormId = Selection->FormId;
235 UiMenuList->QuestionId = Selection->QuestionId;
236
237 InsertHeadList (&gMenuList, &UiMenuList->MenuLink);
238 }
239
240
241 /**
242 Free Menu option linked list.
243
244 **/
245 VOID
246 UiFreeMenu (
247 VOID
248 )
249 {
250 UI_MENU_OPTION *MenuOption;
251
252 while (!IsListEmpty (&Menu)) {
253 MenuOption = MENU_OPTION_FROM_LINK (Menu.ForwardLink);
254 RemoveEntryList (&MenuOption->Link);
255
256 //
257 // We allocated space for this description when we did a GetToken, free it here
258 //
259 if (MenuOption->Skip != 0) {
260 //
261 // For date/time, MenuOption->Description is shared by three Menu Options
262 // Data format : [01/02/2004] [11:22:33]
263 // Line number : 0 0 1 0 0 1
264 //
265 gBS->FreePool (MenuOption->Description);
266 }
267 gBS->FreePool (MenuOption);
268 }
269 }
270
271
272 /**
273 Free Menu option linked list.
274
275 **/
276 VOID
277 UiFreeRefreshList (
278 VOID
279 )
280 {
281 MENU_REFRESH_ENTRY *OldMenuRefreshEntry;
282
283 while (gMenuRefreshHead != NULL) {
284 OldMenuRefreshEntry = gMenuRefreshHead->Next;
285 gBS->FreePool (gMenuRefreshHead);
286 gMenuRefreshHead = OldMenuRefreshEntry;
287 }
288
289 gMenuRefreshHead = NULL;
290 }
291
292
293
294 /**
295 Refresh screen.
296
297 **/
298 VOID
299 RefreshForm (
300 VOID
301 )
302 {
303 CHAR16 *OptionString;
304 MENU_REFRESH_ENTRY *MenuRefreshEntry;
305 UINTN Index;
306 UINTN Loop;
307 EFI_STATUS Status;
308 UI_MENU_SELECTION *Selection;
309 FORM_BROWSER_STATEMENT *Question;
310
311 OptionString = NULL;
312
313 if (gMenuRefreshHead != NULL) {
314
315 MenuRefreshEntry = gMenuRefreshHead;
316
317 do {
318 gST->ConOut->SetAttribute (gST->ConOut, MenuRefreshEntry->CurrentAttribute);
319
320 Selection = MenuRefreshEntry->Selection;
321 Question = MenuRefreshEntry->MenuOption->ThisTag;
322
323 //
324 // Don't update Question being edited
325 //
326 if (Question != MenuRefreshEntry->Selection->Statement) {
327
328 Status = GetQuestionValue (Selection->FormSet, Selection->Form, Question, FALSE);
329 if (EFI_ERROR (Status)) {
330 return;
331 }
332
333 ProcessOptions (Selection, MenuRefreshEntry->MenuOption, FALSE, &OptionString);
334
335 if (OptionString != NULL) {
336 //
337 // If leading spaces on OptionString - remove the spaces
338 //
339 for (Index = 0; OptionString[Index] == L' '; Index++)
340 ;
341
342 for (Loop = 0; OptionString[Index] != CHAR_NULL; Index++) {
343 OptionString[Loop] = OptionString[Index];
344 Loop++;
345 }
346
347 OptionString[Loop] = CHAR_NULL;
348
349 PrintStringAt (MenuRefreshEntry->CurrentColumn, MenuRefreshEntry->CurrentRow, OptionString);
350 gBS->FreePool (OptionString);
351 }
352 }
353
354 MenuRefreshEntry = MenuRefreshEntry->Next;
355
356 } while (MenuRefreshEntry != NULL);
357 }
358 }
359
360
361 /**
362 Wait for a given event to fire, or for an optional timeout to expire.
363
364 @param Event The event to wait for
365 @param Timeout An optional timeout value in 100 ns units.
366 @param RefreshInterval Menu refresh interval (in seconds).
367
368 @retval EFI_SUCCESS Event fired before Timeout expired.
369 @retval EFI_TIME_OUT Timout expired before Event fired.
370
371 **/
372 EFI_STATUS
373 UiWaitForSingleEvent (
374 IN EFI_EVENT Event,
375 IN UINT64 Timeout, OPTIONAL
376 IN UINT8 RefreshInterval OPTIONAL
377 )
378 {
379 EFI_STATUS Status;
380 UINTN Index;
381 EFI_EVENT TimerEvent;
382 EFI_EVENT WaitList[2];
383
384 if (Timeout != 0) {
385 //
386 // Create a timer event
387 //
388 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
389 if (!EFI_ERROR (Status)) {
390 //
391 // Set the timer event
392 //
393 gBS->SetTimer (
394 TimerEvent,
395 TimerRelative,
396 Timeout
397 );
398
399 //
400 // Wait for the original event or the timer
401 //
402 WaitList[0] = Event;
403 WaitList[1] = TimerEvent;
404 Status = gBS->WaitForEvent (2, WaitList, &Index);
405 gBS->CloseEvent (TimerEvent);
406
407 //
408 // If the timer expired, change the return to timed out
409 //
410 if (!EFI_ERROR (Status) && Index == 1) {
411 Status = EFI_TIMEOUT;
412 }
413 }
414 } else {
415 //
416 // Update screen every second
417 //
418 if (RefreshInterval == 0) {
419 Timeout = ONE_SECOND;
420 } else {
421 Timeout = RefreshInterval * ONE_SECOND;
422 }
423
424 do {
425 Status = gBS->CreateEvent (EVT_TIMER, 0, NULL, NULL, &TimerEvent);
426
427 //
428 // Set the timer event
429 //
430 gBS->SetTimer (
431 TimerEvent,
432 TimerRelative,
433 Timeout
434 );
435
436 //
437 // Wait for the original event or the timer
438 //
439 WaitList[0] = Event;
440 WaitList[1] = TimerEvent;
441 Status = gBS->WaitForEvent (2, WaitList, &Index);
442
443 //
444 // If the timer expired, update anything that needs a refresh and keep waiting
445 //
446 if (!EFI_ERROR (Status) && Index == 1) {
447 Status = EFI_TIMEOUT;
448 if (RefreshInterval != 0) {
449 RefreshForm ();
450 }
451 }
452
453 gBS->CloseEvent (TimerEvent);
454 } while (Status == EFI_TIMEOUT);
455 }
456
457 return Status;
458 }
459
460
461 /**
462 Add one menu option by specified description and context.
463
464 @param String String description for this option.
465 @param Handle Hii handle for the package list.
466 @param Statement Statement of this Menu Option.
467 @param NumberOfLines Display lines for this Menu Option.
468 @param MenuItemCount The index for this Option in the Menu.
469
470 **/
471 VOID
472 UiAddMenuOption (
473 IN CHAR16 *String,
474 IN EFI_HII_HANDLE Handle,
475 IN FORM_BROWSER_STATEMENT *Statement,
476 IN UINT16 NumberOfLines,
477 IN UINT16 MenuItemCount
478 )
479 {
480 UI_MENU_OPTION *MenuOption;
481 UINTN Index;
482 UINTN Count;
483
484 Count = 1;
485
486 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
487 //
488 // Add three MenuOptions for Date/Time
489 // Data format : [01/02/2004] [11:22:33]
490 // Line number : 0 0 1 0 0 1
491 //
492 NumberOfLines = 0;
493 Count = 3;
494
495 if (Statement->Storage == NULL) {
496 //
497 // For RTC type of date/time, set default refresh interval to be 1 second
498 //
499 if (Statement->RefreshInterval == 0) {
500 Statement->RefreshInterval = 1;
501 }
502 }
503 }
504
505 for (Index = 0; Index < Count; Index++) {
506 MenuOption = AllocateZeroPool (sizeof (UI_MENU_OPTION));
507 ASSERT (MenuOption);
508
509 MenuOption->Signature = UI_MENU_OPTION_SIGNATURE;
510 MenuOption->Description = String;
511 MenuOption->Handle = Handle;
512 MenuOption->ThisTag = Statement;
513 MenuOption->EntryNumber = MenuItemCount;
514
515 if (Index == 2) {
516 //
517 // Override LineNumber for the MenuOption in Date/Time sequence
518 //
519 MenuOption->Skip = 1;
520 } else {
521 MenuOption->Skip = NumberOfLines;
522 }
523 MenuOption->Sequence = Index;
524
525 if (Statement->GrayOutExpression != NULL) {
526 MenuOption->GrayOut = Statement->GrayOutExpression->Result.Value.b;
527 }
528
529 if ((Statement->ValueExpression != NULL) ||
530 ((Statement->QuestionFlags & EFI_IFR_FLAG_READ_ONLY) != 0)) {
531 MenuOption->ReadOnly = TRUE;
532 }
533
534 InsertTailList (&Menu, &MenuOption->Link);
535 }
536 }
537
538
539 /**
540 Routine used to abstract a generic dialog interface and return the selected key or string
541
542 @param NumberOfLines The number of lines for the dialog box
543 @param HotKey Defines whether a single character is parsed
544 (TRUE) and returned in KeyValue or a string is
545 returned in StringBuffer. Two special characters
546 are considered when entering a string, a SCAN_ESC
547 and an CHAR_CARRIAGE_RETURN. SCAN_ESC terminates
548 string input and returns
549 @param MaximumStringSize The maximum size in bytes of a typed in string
550 (each character is a CHAR16) and the minimum
551 string returned is two bytes
552 @param StringBuffer The passed in pointer to the buffer which will
553 hold the typed in string if HotKey is FALSE
554 @param KeyValue The EFI_KEY value returned if HotKey is TRUE..
555 @param ... A series of (quantity == NumberOfLines) text
556 strings which will be used to construct the dialog
557 box
558
559 @retval EFI_SUCCESS Displayed dialog and received user interaction
560 @retval EFI_INVALID_PARAMETER One of the parameters was invalid (e.g.
561 (StringBuffer == NULL) && (HotKey == FALSE))
562 @retval EFI_DEVICE_ERROR User typed in an ESC character to exit the routine
563
564 **/
565 EFI_STATUS
566 CreateDialog (
567 IN UINTN NumberOfLines,
568 IN BOOLEAN HotKey,
569 IN UINTN MaximumStringSize,
570 OUT CHAR16 *StringBuffer,
571 OUT EFI_INPUT_KEY *KeyValue,
572 ...
573 )
574 {
575 VA_LIST Marker;
576 VA_LIST MarkerBackup;
577 UINTN Count;
578 EFI_INPUT_KEY Key;
579 UINTN LargestString;
580 CHAR16 *TempString;
581 CHAR16 *BufferedString;
582 CHAR16 *StackString;
583 CHAR16 KeyPad[2];
584 UINTN Start;
585 UINTN Top;
586 UINTN Index;
587 EFI_STATUS Status;
588 BOOLEAN SelectionComplete;
589 UINTN InputOffset;
590 UINTN CurrentAttribute;
591 UINTN DimensionsWidth;
592 UINTN DimensionsHeight;
593
594 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
595 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
596
597 SelectionComplete = FALSE;
598 InputOffset = 0;
599 TempString = AllocateZeroPool (MaximumStringSize * 2);
600 BufferedString = AllocateZeroPool (MaximumStringSize * 2);
601 CurrentAttribute = gST->ConOut->Mode->Attribute;
602
603 ASSERT (TempString);
604 ASSERT (BufferedString);
605
606 VA_START (Marker, KeyValue);
607 MarkerBackup = Marker;
608
609 //
610 // Zero the outgoing buffer
611 //
612 ZeroMem (StringBuffer, MaximumStringSize);
613
614 if (HotKey) {
615 if (KeyValue == NULL) {
616 return EFI_INVALID_PARAMETER;
617 }
618 } else {
619 if (StringBuffer == NULL) {
620 return EFI_INVALID_PARAMETER;
621 }
622 }
623 //
624 // Disable cursor
625 //
626 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
627
628 LargestString = 0;
629
630 //
631 // Determine the largest string in the dialog box
632 // Notice we are starting with 1 since String is the first string
633 //
634 for (Count = 0; Count < NumberOfLines; Count++) {
635 StackString = VA_ARG (Marker, CHAR16 *);
636
637 if (StackString[0] == L' ') {
638 InputOffset = Count + 1;
639 }
640
641 if ((GetStringWidth (StackString) / 2) > LargestString) {
642 //
643 // Size of the string visually and subtract the width by one for the null-terminator
644 //
645 LargestString = (GetStringWidth (StackString) / 2);
646 }
647 }
648
649 Start = (DimensionsWidth - LargestString - 2) / 2 + gScreenDimensions.LeftColumn + 1;
650 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
651
652 Count = 0;
653
654 //
655 // Display the Popup
656 //
657 CreateSharedPopUp (LargestString, NumberOfLines, MarkerBackup);
658
659 //
660 // Take the first key typed and report it back?
661 //
662 if (HotKey) {
663 Status = WaitForKeyStroke (&Key);
664 ASSERT_EFI_ERROR (Status);
665 CopyMem (KeyValue, &Key, sizeof (EFI_INPUT_KEY));
666
667 } else {
668 do {
669 Status = WaitForKeyStroke (&Key);
670
671 switch (Key.UnicodeChar) {
672 case CHAR_NULL:
673 switch (Key.ScanCode) {
674 case SCAN_ESC:
675 gBS->FreePool (TempString);
676 gBS->FreePool (BufferedString);
677 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
678 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
679 return EFI_DEVICE_ERROR;
680
681 default:
682 break;
683 }
684
685 break;
686
687 case CHAR_CARRIAGE_RETURN:
688 SelectionComplete = TRUE;
689 gBS->FreePool (TempString);
690 gBS->FreePool (BufferedString);
691 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
692 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
693 return EFI_SUCCESS;
694 break;
695
696 case CHAR_BACKSPACE:
697 if (StringBuffer[0] != CHAR_NULL) {
698 for (Index = 0; StringBuffer[Index] != CHAR_NULL; Index++) {
699 TempString[Index] = StringBuffer[Index];
700 }
701 //
702 // Effectively truncate string by 1 character
703 //
704 TempString[Index - 1] = CHAR_NULL;
705 StrCpy (StringBuffer, TempString);
706 }
707
708 default:
709 //
710 // If it is the beginning of the string, don't worry about checking maximum limits
711 //
712 if ((StringBuffer[0] == CHAR_NULL) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
713 StrnCpy (StringBuffer, &Key.UnicodeChar, 1);
714 StrnCpy (TempString, &Key.UnicodeChar, 1);
715 } else if ((GetStringWidth (StringBuffer) < MaximumStringSize) && (Key.UnicodeChar != CHAR_BACKSPACE)) {
716 KeyPad[0] = Key.UnicodeChar;
717 KeyPad[1] = CHAR_NULL;
718 StrCat (StringBuffer, KeyPad);
719 StrCat (TempString, KeyPad);
720 }
721 //
722 // If the width of the input string is now larger than the screen, we nee to
723 // adjust the index to start printing portions of the string
724 //
725 SetUnicodeMem (BufferedString, LargestString, L' ');
726
727 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
728
729 if ((GetStringWidth (StringBuffer) / 2) > (DimensionsWidth - 2)) {
730 Index = (GetStringWidth (StringBuffer) / 2) - DimensionsWidth + 2;
731 } else {
732 Index = 0;
733 }
734
735 for (Count = 0; Index + 1 < GetStringWidth (StringBuffer) / 2; Index++, Count++) {
736 BufferedString[Count] = StringBuffer[Index];
737 }
738
739 PrintStringAt (Start + 1, Top + InputOffset, BufferedString);
740 break;
741 }
742 } while (!SelectionComplete);
743 }
744
745 gST->ConOut->SetAttribute (gST->ConOut, CurrentAttribute);
746 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
747 return EFI_SUCCESS;
748 }
749
750 /**
751 Draw a pop up windows based on the dimension, number of lines and
752 strings specified.
753
754 @param RequestedWidth The width of the pop-up.
755 @param NumberOfLines The number of lines.
756 @param Marker The variable argument list for the list of string to be printed.
757
758 **/
759 VOID
760 CreateSharedPopUp (
761 IN UINTN RequestedWidth,
762 IN UINTN NumberOfLines,
763 IN VA_LIST Marker
764 )
765 {
766 UINTN Index;
767 UINTN Count;
768 CHAR16 Character;
769 UINTN Start;
770 UINTN End;
771 UINTN Top;
772 UINTN Bottom;
773 CHAR16 *String;
774 UINTN DimensionsWidth;
775 UINTN DimensionsHeight;
776
777 DimensionsWidth = gScreenDimensions.RightColumn - gScreenDimensions.LeftColumn;
778 DimensionsHeight = gScreenDimensions.BottomRow - gScreenDimensions.TopRow;
779
780 gST->ConOut->SetAttribute (gST->ConOut, POPUP_TEXT | POPUP_BACKGROUND);
781
782 if ((RequestedWidth + 2) > DimensionsWidth) {
783 RequestedWidth = DimensionsWidth - 2;
784 }
785
786 //
787 // Subtract the PopUp width from total Columns, allow for one space extra on
788 // each end plus a border.
789 //
790 Start = (DimensionsWidth - RequestedWidth - 2) / 2 + gScreenDimensions.LeftColumn + 1;
791 End = Start + RequestedWidth + 1;
792
793 Top = ((DimensionsHeight - NumberOfLines - 2) / 2) + gScreenDimensions.TopRow - 1;
794 Bottom = Top + NumberOfLines + 2;
795
796 Character = BOXDRAW_DOWN_RIGHT;
797 PrintCharAt (Start, Top, Character);
798 Character = BOXDRAW_HORIZONTAL;
799 for (Index = Start; Index + 2 < End; Index++) {
800 PrintChar (Character);
801 }
802
803 Character = BOXDRAW_DOWN_LEFT;
804 PrintChar (Character);
805 Character = BOXDRAW_VERTICAL;
806
807 Count = 0;
808 for (Index = Top; Index + 2 < Bottom; Index++, Count++) {
809 String = VA_ARG (Marker, CHAR16*);
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 ... A series of text strings that displayed in the pop-up.
862
863 **/
864 VOID
865 CreatePopUp (
866 IN UINTN RequestedWidth,
867 IN UINTN NumberOfLines,
868 ...
869 )
870 {
871 VA_LIST Marker;
872
873 VA_START (Marker, NumberOfLines);
874
875 CreateSharedPopUp (RequestedWidth, NumberOfLines, Marker);
876
877 VA_END (Marker);
878 }
879
880
881 /**
882 Update status bar on the bottom of menu.
883
884 @param MessageType The type of message to be shown.
885 @param Flags The flags in Question header.
886 @param State Set or clear.
887
888 **/
889 VOID
890 UpdateStatusBar (
891 IN UINTN MessageType,
892 IN UINT8 Flags,
893 IN BOOLEAN State
894 )
895 {
896 UINTN Index;
897 CHAR16 *NvUpdateMessage;
898 CHAR16 *InputErrorMessage;
899
900 NvUpdateMessage = GetToken (STRING_TOKEN (NV_UPDATE_MESSAGE), gHiiHandle);
901 InputErrorMessage = GetToken (STRING_TOKEN (INPUT_ERROR_MESSAGE), gHiiHandle);
902
903 switch (MessageType) {
904 case INPUT_ERROR:
905 if (State) {
906 gST->ConOut->SetAttribute (gST->ConOut, ERROR_TEXT);
907 PrintStringAt (
908 gScreenDimensions.LeftColumn + gPromptBlockWidth,
909 gScreenDimensions.BottomRow - 1,
910 InputErrorMessage
911 );
912 mInputError = TRUE;
913 } else {
914 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
915 for (Index = 0; Index < (GetStringWidth (InputErrorMessage) - 2) / 2; Index++) {
916 PrintAt (gScreenDimensions.LeftColumn + gPromptBlockWidth + Index, gScreenDimensions.BottomRow - 1, L" ");
917 }
918
919 mInputError = FALSE;
920 }
921 break;
922
923 case NV_UPDATE_REQUIRED:
924 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
925 if (State) {
926 gST->ConOut->SetAttribute (gST->ConOut, INFO_TEXT);
927 PrintStringAt (
928 gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth,
929 gScreenDimensions.BottomRow - 1,
930 NvUpdateMessage
931 );
932 gResetRequired = (BOOLEAN) (gResetRequired | ((Flags & EFI_IFR_FLAG_RESET_REQUIRED) == EFI_IFR_FLAG_RESET_REQUIRED));
933
934 gNvUpdateRequired = TRUE;
935 } else {
936 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_HIGHLIGHT);
937 for (Index = 0; Index < (GetStringWidth (NvUpdateMessage) - 2) / 2; Index++) {
938 PrintAt (
939 (gScreenDimensions.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + Index),
940 gScreenDimensions.BottomRow - 1,
941 L" "
942 );
943 }
944
945 gNvUpdateRequired = FALSE;
946 }
947 }
948 break;
949
950 case REFRESH_STATUS_BAR:
951 if (mInputError) {
952 UpdateStatusBar (INPUT_ERROR, Flags, TRUE);
953 }
954
955 if (gNvUpdateRequired) {
956 UpdateStatusBar (NV_UPDATE_REQUIRED, Flags, TRUE);
957 }
958 break;
959
960 default:
961 break;
962 }
963
964 gBS->FreePool (InputErrorMessage);
965 gBS->FreePool (NvUpdateMessage);
966 return ;
967 }
968
969
970 /**
971 Get the supported width for a particular op-code
972
973 @param Statement The FORM_BROWSER_STATEMENT structure passed in.
974 @param Handle The handle in the HII database being used
975
976 @return Returns the number of CHAR16 characters that is support.
977
978 **/
979 UINT16
980 GetWidth (
981 IN FORM_BROWSER_STATEMENT *Statement,
982 IN EFI_HII_HANDLE Handle
983 )
984 {
985 CHAR16 *String;
986 UINTN Size;
987 UINT16 Width;
988
989 Size = 0;
990
991 //
992 // See if the second text parameter is really NULL
993 //
994 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
995 String = GetToken (Statement->TextTwo, Handle);
996 Size = StrLen (String);
997 gBS->FreePool (String);
998 }
999
1000 if ((Statement->Operand == EFI_IFR_SUBTITLE_OP) ||
1001 (Statement->Operand == EFI_IFR_REF_OP) ||
1002 (Statement->Operand == EFI_IFR_PASSWORD_OP) ||
1003 (Statement->Operand == EFI_IFR_ACTION_OP) ||
1004 (Statement->Operand == EFI_IFR_RESET_BUTTON_OP) ||
1005 //
1006 // Allow a wide display if text op-code and no secondary text op-code
1007 //
1008 ((Statement->Operand == EFI_IFR_TEXT_OP) && (Size == 0))
1009 ) {
1010 Width = (UINT16) (gPromptBlockWidth + gOptionBlockWidth);
1011 } else {
1012 Width = (UINT16) gPromptBlockWidth;
1013 }
1014
1015 if (Statement->InSubtitle) {
1016 Width -= SUBTITLE_INDENT;
1017 }
1018
1019 return Width;
1020 }
1021
1022
1023 BOOLEAN GetLineByWidthFinished = FALSE;
1024
1025 /**
1026 Will copy LineWidth amount of a string in the OutputString buffer and return the
1027 number of CHAR16 characters that were copied into the OutputString buffer.
1028
1029 @param InputString String description for this option.
1030 @param LineWidth Width of the desired string to extract in CHAR16
1031 characters
1032 @param Index Where in InputString to start the copy process
1033 @param OutputString Buffer to copy the string into
1034
1035 @return Returns the number of CHAR16 characters that were copied into the OutputString buffer.
1036
1037 **/
1038 UINT16
1039 GetLineByWidth (
1040 IN CHAR16 *InputString,
1041 IN UINT16 LineWidth,
1042 IN OUT UINTN *Index,
1043 OUT CHAR16 **OutputString
1044 )
1045 {
1046 UINT16 Count;
1047 UINT16 Count2;
1048
1049 if (GetLineByWidthFinished) {
1050 GetLineByWidthFinished = FALSE;
1051 return (UINT16) 0;
1052 }
1053
1054 Count = LineWidth;
1055 Count2 = 0;
1056
1057 *OutputString = AllocateZeroPool (((UINTN) (LineWidth + 1) * 2));
1058
1059 //
1060 // Ensure we have got a valid buffer
1061 //
1062 if (*OutputString != NULL) {
1063
1064 //
1065 //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.
1066 //To avoid displaying this empty line in screen, just skip the two CHARs here.
1067 //
1068 if ((InputString[*Index] == NARROW_CHAR) && (InputString[*Index + 1] == CHAR_CARRIAGE_RETURN)) {
1069 *Index = *Index + 2;
1070 }
1071
1072 //
1073 // Fast-forward the string and see if there is a carriage-return in the string
1074 //
1075 for (; (InputString[*Index + Count2] != CHAR_CARRIAGE_RETURN) && (Count2 != LineWidth); Count2++)
1076 ;
1077
1078 //
1079 // Copy the desired LineWidth of data to the output buffer.
1080 // Also make sure that we don't copy more than the string.
1081 // Also make sure that if there are linefeeds, we account for them.
1082 //
1083 if ((StrSize (&InputString[*Index]) <= ((UINTN) (LineWidth + 1) * 2)) &&
1084 (StrSize (&InputString[*Index]) <= ((UINTN) (Count2 + 1) * 2))
1085 ) {
1086 //
1087 // Convert to CHAR16 value and show that we are done with this operation
1088 //
1089 LineWidth = (UINT16) ((StrSize (&InputString[*Index]) - 2) / 2);
1090 if (LineWidth != 0) {
1091 GetLineByWidthFinished = TRUE;
1092 }
1093 } else {
1094 if (Count2 == LineWidth) {
1095 //
1096 // Rewind the string from the maximum size until we see a space to break the line
1097 //
1098 for (; (InputString[*Index + LineWidth] != CHAR_SPACE) && (LineWidth != 0); LineWidth--)
1099 ;
1100 if (LineWidth == 0) {
1101 LineWidth = Count;
1102 }
1103 } else {
1104 LineWidth = Count2;
1105 }
1106 }
1107
1108 CopyMem (*OutputString, &InputString[*Index], LineWidth * 2);
1109
1110 //
1111 // If currently pointing to a space, increment the index to the first non-space character
1112 //
1113 for (;
1114 (InputString[*Index + LineWidth] == CHAR_SPACE) || (InputString[*Index + LineWidth] == CHAR_CARRIAGE_RETURN);
1115 (*Index)++
1116 )
1117 ;
1118 *Index = (UINT16) (*Index + LineWidth);
1119 return LineWidth;
1120 } else {
1121 return (UINT16) 0;
1122 }
1123 }
1124
1125
1126 /**
1127 Update display lines for a Menu Option.
1128
1129 @param Selection The user's selection.
1130 @param MenuOption The MenuOption to be checked.
1131 @param OptionalString The option string.
1132 @param SkipValue The number of lins to skip.
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 if (OptionString != NULL) {
2142 FreePool (OptionString);
2143 }
2144 }
2145 break;
2146
2147 case '^':
2148 ScreenOperation = UiUp;
2149 break;
2150
2151 case 'V':
2152 case 'v':
2153 ScreenOperation = UiDown;
2154 break;
2155
2156 case ' ':
2157 if (gClassOfVfr != EFI_FRONT_PAGE_SUBCLASS) {
2158 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) {
2159 ScreenOperation = UiSelect;
2160 }
2161 }
2162 break;
2163
2164 case CHAR_NULL:
2165 if (((Key.ScanCode == SCAN_F1) && ((gFunctionKeySetting & FUNCTION_ONE) != FUNCTION_ONE)) ||
2166 ((Key.ScanCode == SCAN_F2) && ((gFunctionKeySetting & FUNCTION_TWO) != FUNCTION_TWO)) ||
2167 ((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2168 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2169 ) {
2170 //
2171 // If the function key has been disabled, just ignore the key.
2172 //
2173 } else {
2174 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2175 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2176 if (Key.ScanCode == SCAN_F9) {
2177 //
2178 // Reset to standard default
2179 //
2180 DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD;
2181 }
2182 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2183 break;
2184 }
2185 }
2186 }
2187 break;
2188 }
2189 break;
2190
2191 case CfScreenOperation:
2192 if (ScreenOperation != UiPrevious && ScreenOperation != UiReset) {
2193 //
2194 // If the screen has no menu items, and the user didn't select UiPrevious, or UiReset
2195 // ignore the selection and go back to reading keys.
2196 //
2197 if (IsListEmpty (&Menu)) {
2198 ControlFlag = CfReadKey;
2199 break;
2200 }
2201 //
2202 // if there is nothing logical to place a cursor on, just move on to wait for a key.
2203 //
2204 for (Link = Menu.ForwardLink; Link != &Menu; Link = Link->ForwardLink) {
2205 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2206 if (IsSelectable (NextMenuOption)) {
2207 break;
2208 }
2209 }
2210
2211 if (Link == &Menu) {
2212 ControlFlag = CfPrepareToReadKey;
2213 break;
2214 }
2215 } else if (ScreenOperation == UiReset) {
2216 //
2217 // Press ESC to exit FormSet
2218 //
2219 Selection->Action = UI_ACTION_EXIT;
2220 Selection->Statement = NULL;
2221 }
2222
2223 for (Index = 0;
2224 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2225 Index++
2226 ) {
2227 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2228 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2229 break;
2230 }
2231 }
2232 break;
2233
2234 case CfUiPrevious:
2235 ControlFlag = CfCheckSelection;
2236
2237 if (IsListEmpty (&gMenuList)) {
2238 Selection->Action = UI_ACTION_NONE;
2239 if (IsListEmpty (&Menu)) {
2240 ControlFlag = CfReadKey;
2241 }
2242 break;
2243 }
2244
2245 //
2246 // Remove the Cached page entry
2247 //
2248 UiRemoveMenuListEntry (Selection);
2249
2250 Selection->Action = UI_ACTION_REFRESH_FORM;
2251 Selection->Statement = NULL;
2252 break;
2253
2254 case CfUiSelect:
2255 ControlFlag = CfCheckSelection;
2256
2257 Statement = MenuOption->ThisTag;
2258 if ((Statement->Operand == EFI_IFR_TEXT_OP) ||
2259 (Statement->Operand == EFI_IFR_DATE_OP) ||
2260 (Statement->Operand == EFI_IFR_TIME_OP) ||
2261 (Statement->Operand == EFI_IFR_NUMERIC_OP && Statement->Step != 0)) {
2262 break;
2263 }
2264
2265 //
2266 // Keep highlight on current MenuOption
2267 //
2268 Selection->QuestionId = Statement->QuestionId;
2269
2270 switch (Statement->Operand) {
2271 case EFI_IFR_REF_OP:
2272 if (Statement->RefDevicePath != 0) {
2273 //
2274 // Goto another Hii Package list
2275 //
2276 ControlFlag = CfUiReset;
2277 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2278
2279 StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle);
2280 if (StringPtr == NULL) {
2281 //
2282 // No device path string not found, exit
2283 //
2284 Selection->Action = UI_ACTION_EXIT;
2285 Selection->Statement = NULL;
2286 break;
2287 }
2288 BufferSize = StrLen (StringPtr) / 2;
2289 DevicePath = AllocatePool (BufferSize);
2290
2291 HexStringToBufInReverseOrder ((UINT8 *) DevicePath, &BufferSize, StringPtr);
2292 Selection->Handle = HiiLibDevicePathToHiiHandle (DevicePath);
2293 if (Selection->Handle == NULL) {
2294 //
2295 // If target Hii Handle not found, exit
2296 //
2297 Selection->Action = UI_ACTION_EXIT;
2298 Selection->Statement = NULL;
2299 break;
2300 }
2301
2302 gBS->FreePool (StringPtr);
2303 gBS->FreePool (DevicePath);
2304
2305 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2306 Selection->FormId = Statement->RefFormId;
2307 Selection->QuestionId = Statement->RefQuestionId;
2308 } else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) {
2309 //
2310 // Goto another Formset, check for uncommitted data
2311 //
2312 ControlFlag = CfUiReset;
2313 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2314
2315 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2316 Selection->FormId = Statement->RefFormId;
2317 Selection->QuestionId = Statement->RefQuestionId;
2318 } else if (Statement->RefFormId != 0) {
2319 //
2320 // Goto another form inside this formset,
2321 //
2322 Selection->Action = UI_ACTION_REFRESH_FORM;
2323
2324 //
2325 // Link current form so that we can always go back when someone hits the UiPrevious
2326 //
2327 UiAddMenuListEntry (Selection);
2328
2329 Selection->FormId = Statement->RefFormId;
2330 Selection->QuestionId = Statement->RefQuestionId;
2331 } else if (Statement->RefQuestionId != 0) {
2332 //
2333 // Goto another Question
2334 //
2335 Selection->QuestionId = Statement->RefQuestionId;
2336
2337 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2338 Selection->Action = UI_ACTION_REFRESH_FORM;
2339 } else {
2340 Repaint = TRUE;
2341 NewLine = TRUE;
2342 break;
2343 }
2344 }
2345 break;
2346
2347 case EFI_IFR_ACTION_OP:
2348 //
2349 // Process the Config string <ConfigResp>
2350 //
2351 Status = ProcessQuestionConfig (Selection, Statement);
2352
2353 if (EFI_ERROR (Status)) {
2354 break;
2355 }
2356
2357 //
2358 // The action button may change some Question value, so refresh the form
2359 //
2360 Selection->Action = UI_ACTION_REFRESH_FORM;
2361 break;
2362
2363 case EFI_IFR_RESET_BUTTON_OP:
2364 //
2365 // Reset Question to default value specified by DefaultId
2366 //
2367 ControlFlag = CfUiDefault;
2368 DefaultId = Statement->DefaultId;
2369 break;
2370
2371 default:
2372 //
2373 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2374 //
2375 UpdateKeyHelp (MenuOption, TRUE);
2376 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2377
2378 if (EFI_ERROR (Status)) {
2379 Repaint = TRUE;
2380 NewLine = TRUE;
2381 break;
2382 }
2383
2384 if (OptionString != NULL) {
2385 PrintStringAt (LocalScreen.LeftColumn + gPromptBlockWidth + 1, MenuOption->Row, OptionString);
2386 gBS->FreePool (OptionString);
2387 }
2388
2389 Selection->Action = UI_ACTION_REFRESH_FORM;
2390 break;
2391 }
2392 break;
2393
2394 case CfUiReset:
2395 //
2396 // We are going to leave current FormSet, so check uncommited data in this FormSet
2397 //
2398 ControlFlag = CfCheckSelection;
2399
2400 if (gClassOfVfr == EFI_FRONT_PAGE_SUBCLASS) {
2401 //
2402 // There is no parent menu for FrontPage
2403 //
2404 Selection->Action = UI_ACTION_NONE;
2405 Selection->Statement = MenuOption->ThisTag;
2406 break;
2407 }
2408
2409 //
2410 // If NV flag is up, prompt user
2411 //
2412 if (gNvUpdateRequired) {
2413 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2414
2415 YesResponse = gYesResponse[0];
2416 NoResponse = gNoResponse[0];
2417
2418 do {
2419 CreateDialog (3, TRUE, 0, NULL, &Key, gEmptyString, gAreYouSure, gEmptyString);
2420 } while
2421 (
2422 (Key.ScanCode != SCAN_ESC) &&
2423 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (NoResponse | UPPER_LOWER_CASE_OFFSET)) &&
2424 ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) != (YesResponse | UPPER_LOWER_CASE_OFFSET))
2425 );
2426
2427 //
2428 // If the user hits the YesResponse key
2429 //
2430 if ((Key.UnicodeChar | UPPER_LOWER_CASE_OFFSET) == (YesResponse | UPPER_LOWER_CASE_OFFSET)) {
2431 } else {
2432 Repaint = TRUE;
2433 NewLine = TRUE;
2434
2435 Selection->Action = UI_ACTION_NONE;
2436 break;
2437 }
2438 }
2439
2440 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2441 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2442
2443 UiFreeMenuList ();
2444 gST->ConOut->ClearScreen (gST->ConOut);
2445 return EFI_SUCCESS;
2446
2447 case CfUiLeft:
2448 ControlFlag = CfCheckSelection;
2449 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2450 if (MenuOption->Sequence != 0) {
2451 //
2452 // In the middle or tail of the Date/Time op-code set, go left.
2453 //
2454 NewPos = NewPos->BackLink;
2455 }
2456 }
2457 break;
2458
2459 case CfUiRight:
2460 ControlFlag = CfCheckSelection;
2461 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2462 if (MenuOption->Sequence != 2) {
2463 //
2464 // In the middle or tail of the Date/Time op-code set, go left.
2465 //
2466 NewPos = NewPos->ForwardLink;
2467 }
2468 }
2469 break;
2470
2471 case CfUiUp:
2472 ControlFlag = CfCheckSelection;
2473
2474 SavedListEntry = TopOfScreen;
2475
2476 if (NewPos->BackLink != &Menu) {
2477 NewLine = TRUE;
2478 //
2479 // Adjust Date/Time position before we advance forward.
2480 //
2481 AdjustDateAndTimePosition (TRUE, &NewPos);
2482
2483 //
2484 // Caution that we have already rewind to the top, don't go backward in this situation.
2485 //
2486 if (NewPos->BackLink != &Menu) {
2487 NewPos = NewPos->BackLink;
2488 }
2489
2490 PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2491 DistanceValue = PreviousMenuOption->Skip;
2492
2493 //
2494 // Since the behavior of hitting the up arrow on a Date/Time op-code is intended
2495 // to be one that back to the previous set of op-codes, we need to advance to the sencond
2496 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2497 // checking can be done.
2498 //
2499 DistanceValue += AdjustDateAndTimePosition (TRUE, &NewPos);
2500
2501 //
2502 // Check the previous menu entry to see if it was a zero-length advance. If it was,
2503 // don't worry about a redraw.
2504 //
2505 if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
2506 Repaint = TRUE;
2507 TopOfScreen = NewPos;
2508 }
2509
2510 Difference = MoveToNextStatement (TRUE, &NewPos);
2511 if ((INTN) MenuOption->Row - (INTN) DistanceValue < (INTN) TopRow) {
2512 if (Difference > 0) {
2513 //
2514 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2515 //
2516 TopOfScreen = NewPos;
2517 Repaint = TRUE;
2518 }
2519 }
2520 if (Difference < 0) {
2521 //
2522 // We want to goto previous MenuOption, but finally we go down.
2523 // it means that we hit the begining MenuOption that can be focused
2524 // so we simply scroll to the top
2525 //
2526 if (SavedListEntry != Menu.ForwardLink) {
2527 TopOfScreen = Menu.ForwardLink;
2528 Repaint = TRUE;
2529 }
2530 }
2531
2532 //
2533 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2534 //
2535 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2536
2537 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2538 } else {
2539 SavedMenuOption = MenuOption;
2540 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2541 if (!IsSelectable (MenuOption)) {
2542 //
2543 // If we are at the end of the list and sitting on a text op, we need to more forward
2544 //
2545 ScreenOperation = UiDown;
2546 ControlFlag = CfScreenOperation;
2547 break;
2548 }
2549
2550 MenuOption = SavedMenuOption;
2551 }
2552 break;
2553
2554 case CfUiPageUp:
2555 ControlFlag = CfCheckSelection;
2556
2557 if (NewPos->BackLink == &Menu) {
2558 NewLine = FALSE;
2559 Repaint = FALSE;
2560 break;
2561 }
2562
2563 NewLine = TRUE;
2564 Repaint = TRUE;
2565 Link = TopOfScreen;
2566 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2567 Index = BottomRow;
2568 while ((Index >= TopRow) && (Link->BackLink != &Menu)) {
2569 Index = Index - PreviousMenuOption->Skip;
2570 Link = Link->BackLink;
2571 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2572 }
2573
2574 TopOfScreen = Link;
2575 Difference = MoveToNextStatement (TRUE, &Link);
2576 if (Difference > 0) {
2577 //
2578 // The focus MenuOption is above the TopOfScreen
2579 //
2580 TopOfScreen = Link;
2581 } else if (Difference < 0) {
2582 //
2583 // This happens when there is no MenuOption can be focused from
2584 // Current MenuOption to the first MenuOption
2585 //
2586 TopOfScreen = Menu.ForwardLink;
2587 }
2588 Index += Difference;
2589 if (Index < TopRow) {
2590 MenuOption = NULL;
2591 }
2592
2593 if (NewPos == Link) {
2594 Repaint = FALSE;
2595 NewLine = FALSE;
2596 } else {
2597 NewPos = Link;
2598 }
2599
2600 //
2601 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2602 // Don't do this when we are already in the first page.
2603 //
2604 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2605 AdjustDateAndTimePosition (TRUE, &NewPos);
2606 break;
2607
2608 case CfUiPageDown:
2609 ControlFlag = CfCheckSelection;
2610
2611 if (NewPos->ForwardLink == &Menu) {
2612 NewLine = FALSE;
2613 Repaint = FALSE;
2614 break;
2615 }
2616
2617 NewLine = TRUE;
2618 Repaint = TRUE;
2619 Link = TopOfScreen;
2620 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2621 Index = TopRow;
2622 while ((Index <= BottomRow) && (Link->ForwardLink != &Menu)) {
2623 Index = Index + NextMenuOption->Skip;
2624 Link = Link->ForwardLink;
2625 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2626 }
2627
2628 Index += MoveToNextStatement (FALSE, &Link);
2629 if (Index > BottomRow) {
2630 //
2631 // There are more MenuOption needing scrolling
2632 //
2633 TopOfScreen = Link;
2634 MenuOption = NULL;
2635 }
2636 if (NewPos == Link && Index <= BottomRow) {
2637 //
2638 // Finally we know that NewPos is the last MenuOption can be focused.
2639 //
2640 NewLine = FALSE;
2641 Repaint = FALSE;
2642 } else {
2643 NewPos = Link;
2644 }
2645
2646 //
2647 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2648 // Don't do this when we are already in the last page.
2649 //
2650 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2651 AdjustDateAndTimePosition (TRUE, &NewPos);
2652 break;
2653
2654 case CfUiDown:
2655 ControlFlag = CfCheckSelection;
2656 //
2657 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2658 // to be one that progresses to the next set of op-codes, we need to advance to the last
2659 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2660 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2661 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2662 // the Date/Time op-code.
2663 //
2664 SavedListEntry = NewPos;
2665 DistanceValue = AdjustDateAndTimePosition (FALSE, &NewPos);
2666
2667 if (NewPos->ForwardLink != &Menu) {
2668 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2669 NewLine = TRUE;
2670 NewPos = NewPos->ForwardLink;
2671 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2672
2673 DistanceValue += NextMenuOption->Skip;
2674 DistanceValue += MoveToNextStatement (FALSE, &NewPos);
2675 //
2676 // An option might be multi-line, so we need to reflect that data in the overall skip value
2677 //
2678 UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, SkipValue);
2679
2680 Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
2681 if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
2682 (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
2683 NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
2684 ) {
2685 Temp ++;
2686 }
2687
2688 //
2689 // If we are going to scroll, update TopOfScreen
2690 //
2691 if (Temp > BottomRow) {
2692 do {
2693 //
2694 // Is the current top of screen a zero-advance op-code?
2695 // If so, keep moving forward till we hit a >0 advance op-code
2696 //
2697 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2698
2699 //
2700 // If bottom op-code is more than one line or top op-code is more than one line
2701 //
2702 if ((DistanceValue > 1) || (MenuOption->Skip > 1)) {
2703 //
2704 // Is the bottom op-code greater than or equal in size to the top op-code?
2705 //
2706 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
2707 //
2708 // Skip the top op-code
2709 //
2710 TopOfScreen = TopOfScreen->ForwardLink;
2711 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
2712
2713 OldSkipValue = Difference;
2714
2715 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2716
2717 //
2718 // If we have a remainder, skip that many more op-codes until we drain the remainder
2719 //
2720 for (;
2721 Difference >= (INTN) SavedMenuOption->Skip;
2722 Difference = Difference - (INTN) SavedMenuOption->Skip
2723 ) {
2724 //
2725 // Since the Difference is greater than or equal to this op-code's skip value, skip it
2726 //
2727 TopOfScreen = TopOfScreen->ForwardLink;
2728 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2729 if (Difference < (INTN) SavedMenuOption->Skip) {
2730 Difference = SavedMenuOption->Skip - Difference - 1;
2731 break;
2732 } else {
2733 if (Difference == (INTN) SavedMenuOption->Skip) {
2734 TopOfScreen = TopOfScreen->ForwardLink;
2735 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
2736 Difference = SavedMenuOption->Skip - Difference;
2737 break;
2738 }
2739 }
2740 }
2741 //
2742 // Since we will act on this op-code in the next routine, and increment the
2743 // SkipValue, set the skips to one less than what is required.
2744 //
2745 SkipValue = Difference - 1;
2746
2747 } else {
2748 //
2749 // Since we will act on this op-code in the next routine, and increment the
2750 // SkipValue, set the skips to one less than what is required.
2751 //
2752 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
2753 }
2754 } else {
2755 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
2756 TopOfScreen = TopOfScreen->ForwardLink;
2757 break;
2758 } else {
2759 SkipValue = OldSkipValue;
2760 }
2761 }
2762 //
2763 // If the op-code at the top of the screen is more than one line, let's not skip it yet
2764 // Let's set a skip flag to smoothly scroll the top of the screen.
2765 //
2766 if (SavedMenuOption->Skip > 1) {
2767 if (SavedMenuOption == NextMenuOption) {
2768 SkipValue = 0;
2769 } else {
2770 SkipValue++;
2771 }
2772 } else {
2773 SkipValue = 0;
2774 TopOfScreen = TopOfScreen->ForwardLink;
2775 }
2776 } while (SavedMenuOption->Skip == 0);
2777
2778 Repaint = TRUE;
2779 OldSkipValue = SkipValue;
2780 }
2781
2782 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2783
2784 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2785
2786 } else {
2787 SavedMenuOption = MenuOption;
2788 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2789 if (!IsSelectable (MenuOption)) {
2790 //
2791 // If we are at the end of the list and sitting on a text op, we need to more forward
2792 //
2793 ScreenOperation = UiUp;
2794 ControlFlag = CfScreenOperation;
2795 break;
2796 }
2797
2798 MenuOption = SavedMenuOption;
2799 //
2800 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2801 //
2802 AdjustDateAndTimePosition (TRUE, &NewPos);
2803 }
2804 break;
2805
2806 case CfUiSave:
2807 ControlFlag = CfCheckSelection;
2808
2809 //
2810 // Submit the form
2811 //
2812 Status = SubmitForm (Selection->FormSet, Selection->Form);
2813
2814 if (!EFI_ERROR (Status)) {
2815 UpdateStatusBar (INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2816 UpdateStatusBar (NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
2817 } else {
2818 do {
2819 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString);
2820 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2821
2822 Repaint = TRUE;
2823 NewLine = TRUE;
2824 }
2825 break;
2826
2827 case CfUiDefault:
2828 ControlFlag = CfCheckSelection;
2829
2830 Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId);
2831
2832 if (!EFI_ERROR (Status)) {
2833 Selection->Action = UI_ACTION_REFRESH_FORM;
2834 Selection->Statement = NULL;
2835
2836 //
2837 // Show NV update flag on status bar
2838 //
2839 gNvUpdateRequired = TRUE;
2840 }
2841 break;
2842
2843 case CfUiNoOperation:
2844 ControlFlag = CfCheckSelection;
2845 break;
2846
2847 case CfExit:
2848 UiFreeRefreshList ();
2849
2850 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
2851 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
2852 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
2853 gST->ConOut->OutputString (gST->ConOut, L"\n");
2854
2855 return EFI_SUCCESS;
2856
2857 default:
2858 break;
2859 }
2860 }
2861 }