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