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