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