]> git.proxmox.com Git - mirror_edk2.git/blob - MdeModulePkg/Universal/SetupBrowserDxe/Ui.c
Add new "Modal form" opcode, also add 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 UINTN ModalSkipColumn;
1652
1653 CopyMem (&LocalScreen, &gScreenDimensions, sizeof (EFI_SCREEN_DESCRIPTOR));
1654
1655 Status = EFI_SUCCESS;
1656 FormattedString = NULL;
1657 OptionString = NULL;
1658 ScreenOperation = UiNoOperation;
1659 NewLine = TRUE;
1660 MinRefreshInterval = 0;
1661 DefaultId = 0;
1662
1663 OutputString = NULL;
1664 UpArrow = FALSE;
1665 DownArrow = FALSE;
1666 SkipValue = 0;
1667 OldSkipValue = 0;
1668 MenuRefreshEntry = gMenuRefreshHead;
1669
1670 NextMenuOption = NULL;
1671 PreviousMenuOption = NULL;
1672 SavedMenuOption = NULL;
1673 RefForm = NULL;
1674 ModalSkipColumn = (LocalScreen.RightColumn - LocalScreen.LeftColumn) / 6;
1675
1676 ZeroMem (&Key, sizeof (EFI_INPUT_KEY));
1677
1678 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) == FORMSET_CLASS_FRONT_PAGE){
1679 TopRow = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1680 Row = LocalScreen.TopRow + FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1681 } else {
1682 TopRow = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1683 Row = LocalScreen.TopRow + NONE_FRONT_PAGE_HEADER_HEIGHT + SCROLL_ARROW_HEIGHT;
1684 }
1685
1686 if (Selection->Form->ModalForm) {
1687 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS + ModalSkipColumn;
1688 } else {
1689 Col = LocalScreen.LeftColumn + LEFT_SKIPPED_COLUMNS;
1690 }
1691
1692 BottomRow = LocalScreen.BottomRow - STATUS_BAR_HEIGHT - FOOTER_HEIGHT - SCROLL_ARROW_HEIGHT - 1;
1693
1694 Selection->TopRow = TopRow;
1695 Selection->BottomRow = BottomRow;
1696 Selection->PromptCol = Col;
1697 Selection->OptionCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1698 Selection->Statement = NULL;
1699
1700 TopOfScreen = gMenuOption.ForwardLink;
1701 Repaint = TRUE;
1702 MenuOption = NULL;
1703
1704 //
1705 // Find current Menu
1706 //
1707 CurrentMenu = UiFindMenuList (&Selection->FormSetGuid, Selection->FormId);
1708 if (CurrentMenu == NULL) {
1709 //
1710 // Current menu not found, add it to the menu tree
1711 //
1712 CurrentMenu = UiAddMenuList (NULL, &Selection->FormSetGuid, Selection->FormId);
1713 }
1714 ASSERT (CurrentMenu != NULL);
1715 Selection->CurrentMenu = CurrentMenu;
1716
1717 if (Selection->QuestionId == 0) {
1718 //
1719 // Highlight not specified, fetch it from cached menu
1720 //
1721 Selection->QuestionId = CurrentMenu->QuestionId;
1722 }
1723
1724 //
1725 // Init option as the current user's selection
1726 //
1727 InitializedFlag = TRUE;
1728 NewPos = gMenuOption.ForwardLink;
1729
1730 gST->ConOut->EnableCursor (gST->ConOut, FALSE);
1731 UpdateStatusBar (Selection, REFRESH_STATUS_BAR, (UINT8) 0, TRUE);
1732
1733 ControlFlag = CfInitialization;
1734 Selection->Action = UI_ACTION_NONE;
1735 while (TRUE) {
1736 switch (ControlFlag) {
1737 case CfInitialization:
1738 if (IsListEmpty (&gMenuOption)) {
1739 ControlFlag = CfReadKey;
1740 } else {
1741 ControlFlag = CfCheckSelection;
1742 }
1743 break;
1744
1745 case CfCheckSelection:
1746 if (Selection->Action != UI_ACTION_NONE) {
1747 ControlFlag = CfExit;
1748 } else {
1749 ControlFlag = CfRepaint;
1750 }
1751 break;
1752
1753 case CfRepaint:
1754 ControlFlag = CfRefreshHighLight;
1755
1756 if (Repaint) {
1757 //
1758 // Display menu
1759 //
1760 DownArrow = FALSE;
1761 UpArrow = FALSE;
1762 Row = TopRow;
1763
1764 Temp = (UINTN) SkipValue;
1765 Temp2 = (UINTN) SkipValue;
1766
1767 if (Selection->Form->ModalForm) {
1768 ClearLines (
1769 LocalScreen.LeftColumn + ModalSkipColumn,
1770 LocalScreen.LeftColumn + ModalSkipColumn + gPromptBlockWidth + gOptionBlockWidth,
1771 TopRow - SCROLL_ARROW_HEIGHT,
1772 BottomRow + SCROLL_ARROW_HEIGHT,
1773 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
1774 );
1775 } else {
1776 ClearLines (
1777 LocalScreen.LeftColumn,
1778 LocalScreen.RightColumn,
1779 TopRow - SCROLL_ARROW_HEIGHT,
1780 BottomRow + SCROLL_ARROW_HEIGHT,
1781 PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND
1782 );
1783 }
1784 UiFreeRefreshList ();
1785 MinRefreshInterval = 0;
1786
1787 for (Link = TopOfScreen; Link != &gMenuOption; Link = Link->ForwardLink) {
1788 MenuOption = MENU_OPTION_FROM_LINK (Link);
1789 MenuOption->Row = Row;
1790 MenuOption->Col = Col;
1791 if (Selection->Form->ModalForm) {
1792 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn + ModalSkipColumn;
1793 } else {
1794 MenuOption->OptCol = gPromptBlockWidth + 1 + LocalScreen.LeftColumn;
1795 }
1796
1797 Statement = MenuOption->ThisTag;
1798 if (Statement->InSubtitle) {
1799 MenuOption->Col += SUBTITLE_INDENT;
1800 }
1801
1802 if (MenuOption->GrayOut) {
1803 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
1804 } else {
1805 if (Statement->Operand == EFI_IFR_SUBTITLE_OP) {
1806 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
1807 }
1808 }
1809
1810 Width = GetWidth (Statement, MenuOption->Handle);
1811 OriginalRow = Row;
1812
1813 if (Statement->Operand == EFI_IFR_REF_OP && MenuOption->Col >= 2) {
1814 //
1815 // Print Arrow for Goto button.
1816 //
1817 PrintAt (
1818 MenuOption->Col - 2,
1819 Row,
1820 L"%c",
1821 GEOMETRICSHAPE_RIGHT_TRIANGLE
1822 );
1823 }
1824
1825 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
1826 if ((Temp == 0) && (Row <= BottomRow)) {
1827 PrintStringAt (MenuOption->Col, Row, OutputString);
1828 }
1829 //
1830 // If there is more string to process print on the next row and increment the Skip value
1831 //
1832 if (StrLen (&MenuOption->Description[Index]) != 0) {
1833 if (Temp == 0) {
1834 Row++;
1835 }
1836 }
1837
1838 FreePool (OutputString);
1839 if (Temp != 0) {
1840 Temp--;
1841 }
1842 }
1843
1844 Temp = 0;
1845 Row = OriginalRow;
1846
1847 Status = ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
1848 if (EFI_ERROR (Status)) {
1849 //
1850 // Repaint to clear possible error prompt pop-up
1851 //
1852 Repaint = TRUE;
1853 NewLine = TRUE;
1854 ControlFlag = CfRepaint;
1855 break;
1856 }
1857
1858 if (OptionString != NULL) {
1859 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
1860 //
1861 // If leading spaces on OptionString - remove the spaces
1862 //
1863 for (Index = 0; OptionString[Index] == L' '; Index++) {
1864 MenuOption->OptCol++;
1865 }
1866
1867 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
1868 OptionString[Count] = OptionString[Index];
1869 Count++;
1870 }
1871
1872 OptionString[Count] = CHAR_NULL;
1873 }
1874
1875 //
1876 // If Question request refresh, register the op-code
1877 //
1878 if (Statement->RefreshInterval != 0) {
1879 //
1880 // Menu will be refreshed at minimal interval of all Questions
1881 // which have refresh request
1882 //
1883 if (MinRefreshInterval == 0 || Statement->RefreshInterval < MinRefreshInterval) {
1884 MinRefreshInterval = Statement->RefreshInterval;
1885 }
1886
1887 if (gMenuRefreshHead == NULL) {
1888 MenuRefreshEntry = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1889 ASSERT (MenuRefreshEntry != NULL);
1890 MenuRefreshEntry->MenuOption = MenuOption;
1891 MenuRefreshEntry->Selection = Selection;
1892 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1893 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1894 if (MenuOption->GrayOut) {
1895 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
1896 } else {
1897 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
1898 }
1899 gMenuRefreshHead = MenuRefreshEntry;
1900 } else {
1901 //
1902 // Advance to the last entry
1903 //
1904 for (MenuRefreshEntry = gMenuRefreshHead;
1905 MenuRefreshEntry->Next != NULL;
1906 MenuRefreshEntry = MenuRefreshEntry->Next
1907 )
1908 ;
1909 MenuRefreshEntry->Next = AllocateZeroPool (sizeof (MENU_REFRESH_ENTRY));
1910 ASSERT (MenuRefreshEntry->Next != NULL);
1911 MenuRefreshEntry = MenuRefreshEntry->Next;
1912 MenuRefreshEntry->MenuOption = MenuOption;
1913 MenuRefreshEntry->Selection = Selection;
1914 MenuRefreshEntry->CurrentColumn = MenuOption->OptCol;
1915 MenuRefreshEntry->CurrentRow = MenuOption->Row;
1916 if (MenuOption->GrayOut) {
1917 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
1918 } else {
1919 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
1920 }
1921 }
1922 }
1923
1924 Width = (UINT16) gOptionBlockWidth;
1925 OriginalRow = Row;
1926
1927 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
1928 if ((Temp2 == 0) && (Row <= BottomRow)) {
1929 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1930 }
1931 //
1932 // If there is more string to process print on the next row and increment the Skip value
1933 //
1934 if (StrLen (&OptionString[Index]) != 0) {
1935 if (Temp2 == 0) {
1936 Row++;
1937 //
1938 // Since the Number of lines for this menu entry may or may not be reflected accurately
1939 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1940 // some testing to ensure we are keeping this in-sync.
1941 //
1942 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1943 //
1944 if ((Row - OriginalRow) >= MenuOption->Skip) {
1945 MenuOption->Skip++;
1946 }
1947 }
1948 }
1949
1950 FreePool (OutputString);
1951 if (Temp2 != 0) {
1952 Temp2--;
1953 }
1954 }
1955
1956 Temp2 = 0;
1957 Row = OriginalRow;
1958
1959 FreePool (OptionString);
1960 }
1961 //
1962 // If this is a text op with secondary text information
1963 //
1964 if ((Statement->Operand == EFI_IFR_TEXT_OP) && (Statement->TextTwo != 0)) {
1965 StringPtr = GetToken (Statement->TextTwo, MenuOption->Handle);
1966
1967 Width = (UINT16) gOptionBlockWidth;
1968 OriginalRow = Row;
1969
1970 for (Index = 0; GetLineByWidth (StringPtr, Width, &Index, &OutputString) != 0x0000;) {
1971 if ((Temp == 0) && (Row <= BottomRow)) {
1972 PrintStringAt (MenuOption->OptCol, Row, OutputString);
1973 }
1974 //
1975 // If there is more string to process print on the next row and increment the Skip value
1976 //
1977 if (StrLen (&StringPtr[Index]) != 0) {
1978 if (Temp2 == 0) {
1979 Row++;
1980 //
1981 // Since the Number of lines for this menu entry may or may not be reflected accurately
1982 // since the prompt might be 1 lines and option might be many, and vice versa, we need to do
1983 // some testing to ensure we are keeping this in-sync.
1984 //
1985 // If the difference in rows is greater than or equal to the skip value, increase the skip value
1986 //
1987 if ((Row - OriginalRow) >= MenuOption->Skip) {
1988 MenuOption->Skip++;
1989 }
1990 }
1991 }
1992
1993 FreePool (OutputString);
1994 if (Temp2 != 0) {
1995 Temp2--;
1996 }
1997 }
1998
1999 Row = OriginalRow;
2000 FreePool (StringPtr);
2001 }
2002 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2003
2004 //
2005 // Need to handle the bottom of the display
2006 //
2007 if (MenuOption->Skip > 1) {
2008 Row += MenuOption->Skip - SkipValue;
2009 SkipValue = 0;
2010 } else {
2011 Row += MenuOption->Skip;
2012 }
2013
2014 if (Row > BottomRow) {
2015 if (!ValueIsScroll (FALSE, Link)) {
2016 DownArrow = TRUE;
2017 }
2018
2019 Row = BottomRow + 1;
2020 break;
2021 }
2022 }
2023
2024 if (!ValueIsScroll (TRUE, TopOfScreen)) {
2025 UpArrow = TRUE;
2026 }
2027
2028 if (UpArrow) {
2029 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2030 PrintAt (
2031 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2032 TopRow - SCROLL_ARROW_HEIGHT,
2033 L"%c",
2034 ARROW_UP
2035 );
2036 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2037 }
2038
2039 if (DownArrow) {
2040 gST->ConOut->SetAttribute (gST->ConOut, ARROW_TEXT | ARROW_BACKGROUND);
2041 PrintAt (
2042 LocalScreen.LeftColumn + gPromptBlockWidth + gOptionBlockWidth + 1,
2043 BottomRow + SCROLL_ARROW_HEIGHT,
2044 L"%c",
2045 ARROW_DOWN
2046 );
2047 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2048 }
2049
2050 MenuOption = NULL;
2051 }
2052 break;
2053
2054 case CfRefreshHighLight:
2055 //
2056 // MenuOption: Last menu option that need to remove hilight
2057 // MenuOption is set to NULL in Repaint
2058 // NewPos: Current menu option that need to hilight
2059 //
2060 ControlFlag = CfUpdateHelpString;
2061 if (InitializedFlag) {
2062 InitializedFlag = FALSE;
2063 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
2064 }
2065
2066 //
2067 // Repaint flag is normally reset when finish processing CfUpdateHelpString. Temporarily
2068 // reset Repaint flag because we may break halfway and skip CfUpdateHelpString processing.
2069 //
2070 SavedValue = Repaint;
2071 Repaint = FALSE;
2072
2073 if (Selection->QuestionId != 0) {
2074 NewPos = gMenuOption.ForwardLink;
2075 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2076
2077 while (SavedMenuOption->ThisTag->QuestionId != Selection->QuestionId && NewPos->ForwardLink != &gMenuOption) {
2078 NewPos = NewPos->ForwardLink;
2079 SavedMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2080 }
2081 if (SavedMenuOption->ThisTag->QuestionId == Selection->QuestionId) {
2082 //
2083 // Target Question found, find its MenuOption
2084 //
2085 Link = TopOfScreen;
2086
2087 for (Index = TopRow; Index <= BottomRow && Link != NewPos;) {
2088 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2089 Index += SavedMenuOption->Skip;
2090 Link = Link->ForwardLink;
2091 }
2092
2093 if (Link != NewPos || Index > BottomRow) {
2094 //
2095 // NewPos is not in the current page, simply scroll page so that NewPos is in the end of the page
2096 //
2097 Link = NewPos;
2098 for (Index = TopRow; Index <= BottomRow; ) {
2099 Link = Link->BackLink;
2100 SavedMenuOption = MENU_OPTION_FROM_LINK (Link);
2101 Index += SavedMenuOption->Skip;
2102 }
2103 TopOfScreen = Link->ForwardLink;
2104
2105 Repaint = TRUE;
2106 NewLine = TRUE;
2107 ControlFlag = CfRepaint;
2108 break;
2109 }
2110 } else {
2111 //
2112 // Target Question not found, highlight the default menu option
2113 //
2114 NewPos = TopOfScreen;
2115 }
2116
2117 Selection->QuestionId = 0;
2118 }
2119
2120 if (NewPos != NULL && (MenuOption == NULL || NewPos != &MenuOption->Link)) {
2121 if (MenuOption != NULL) {
2122 //
2123 // Remove highlight on last Menu Option
2124 //
2125 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2126 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2127 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2128 if (OptionString != NULL) {
2129 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) ||
2130 (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
2131 ) {
2132 //
2133 // If leading spaces on OptionString - remove the spaces
2134 //
2135 for (Index = 0; OptionString[Index] == L' '; Index++)
2136 ;
2137
2138 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
2139 OptionString[Count] = OptionString[Index];
2140 Count++;
2141 }
2142
2143 OptionString[Count] = CHAR_NULL;
2144 }
2145
2146 Width = (UINT16) gOptionBlockWidth;
2147 OriginalRow = MenuOption->Row;
2148
2149 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
2150 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2151 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2152 }
2153 //
2154 // If there is more string to process print on the next row and increment the Skip value
2155 //
2156 if (StrLen (&OptionString[Index]) != 0) {
2157 MenuOption->Row++;
2158 }
2159
2160 FreePool (OutputString);
2161 }
2162
2163 MenuOption->Row = OriginalRow;
2164
2165 FreePool (OptionString);
2166 } else {
2167 if (NewLine) {
2168 if (MenuOption->GrayOut) {
2169 gST->ConOut->SetAttribute (gST->ConOut, FIELD_TEXT_GRAYED | FIELD_BACKGROUND);
2170 } else if (MenuOption->ThisTag->Operand == EFI_IFR_SUBTITLE_OP) {
2171 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserSubtitleTextColor) | FIELD_BACKGROUND);
2172 }
2173
2174 OriginalRow = MenuOption->Row;
2175 Width = GetWidth (MenuOption->ThisTag, MenuOption->Handle);
2176
2177 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
2178 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2179 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2180 }
2181 //
2182 // If there is more string to process print on the next row and increment the Skip value
2183 //
2184 if (StrLen (&MenuOption->Description[Index]) != 0) {
2185 MenuOption->Row++;
2186 }
2187
2188 FreePool (OutputString);
2189 }
2190
2191 MenuOption->Row = OriginalRow;
2192 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2193 }
2194 }
2195 }
2196
2197 //
2198 // This is the current selected statement
2199 //
2200 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2201 Statement = MenuOption->ThisTag;
2202 Selection->Statement = Statement;
2203 if (!IsSelectable (MenuOption)) {
2204 Repaint = SavedValue;
2205 UpdateKeyHelp (Selection, MenuOption, FALSE);
2206 break;
2207 }
2208
2209 //
2210 // Record highlight for current menu
2211 //
2212 CurrentMenu->QuestionId = Statement->QuestionId;
2213
2214 //
2215 // Set reverse attribute
2216 //
2217 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor));
2218 gST->ConOut->SetCursorPosition (gST->ConOut, MenuOption->Col, MenuOption->Row);
2219
2220 //
2221 // Assuming that we have a refresh linked-list created, lets annotate the
2222 // appropriate entry that we are highlighting with its new attribute. Just prior to this
2223 // lets reset all of the entries' attribute so we do not get multiple highlights in he refresh
2224 //
2225 if (gMenuRefreshHead != NULL) {
2226 for (MenuRefreshEntry = gMenuRefreshHead; MenuRefreshEntry != NULL; MenuRefreshEntry = MenuRefreshEntry->Next) {
2227 if (MenuRefreshEntry->MenuOption->GrayOut) {
2228 MenuRefreshEntry->CurrentAttribute = FIELD_TEXT_GRAYED | FIELD_BACKGROUND;
2229 } else {
2230 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND;
2231 }
2232 if (MenuRefreshEntry->MenuOption == MenuOption) {
2233 MenuRefreshEntry->CurrentAttribute = PcdGet8 (PcdBrowserFieldTextHighlightColor) | PcdGet8 (PcdBrowserFieldBackgroundHighlightColor);
2234 }
2235 }
2236 }
2237
2238 ProcessOptions (Selection, MenuOption, FALSE, &OptionString);
2239 if (OptionString != NULL) {
2240 if (Statement->Operand == EFI_IFR_DATE_OP || Statement->Operand == EFI_IFR_TIME_OP) {
2241 //
2242 // If leading spaces on OptionString - remove the spaces
2243 //
2244 for (Index = 0; OptionString[Index] == L' '; Index++)
2245 ;
2246
2247 for (Count = 0; OptionString[Index] != CHAR_NULL; Index++) {
2248 OptionString[Count] = OptionString[Index];
2249 Count++;
2250 }
2251
2252 OptionString[Count] = CHAR_NULL;
2253 }
2254 Width = (UINT16) gOptionBlockWidth;
2255
2256 OriginalRow = MenuOption->Row;
2257
2258 for (Index = 0; GetLineByWidth (OptionString, Width, &Index, &OutputString) != 0x0000;) {
2259 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2260 PrintStringAt (MenuOption->OptCol, MenuOption->Row, OutputString);
2261 }
2262 //
2263 // If there is more string to process print on the next row and increment the Skip value
2264 //
2265 if (StrLen (&OptionString[Index]) != 0) {
2266 MenuOption->Row++;
2267 }
2268
2269 FreePool (OutputString);
2270 }
2271
2272 MenuOption->Row = OriginalRow;
2273
2274 FreePool (OptionString);
2275 } else {
2276 if (NewLine) {
2277 OriginalRow = MenuOption->Row;
2278
2279 Width = GetWidth (Statement, MenuOption->Handle);
2280
2281 for (Index = 0; GetLineByWidth (MenuOption->Description, Width, &Index, &OutputString) != 0x0000;) {
2282 if (MenuOption->Row >= TopRow && MenuOption->Row <= BottomRow) {
2283 PrintStringAt (MenuOption->Col, MenuOption->Row, OutputString);
2284 }
2285 //
2286 // If there is more string to process print on the next row and increment the Skip value
2287 //
2288 if (StrLen (&MenuOption->Description[Index]) != 0) {
2289 MenuOption->Row++;
2290 }
2291
2292 FreePool (OutputString);
2293 }
2294
2295 MenuOption->Row = OriginalRow;
2296
2297 }
2298 }
2299
2300 UpdateKeyHelp (Selection, MenuOption, FALSE);
2301
2302 //
2303 // Clear reverse attribute
2304 //
2305 gST->ConOut->SetAttribute (gST->ConOut, PcdGet8 (PcdBrowserFieldTextColor) | FIELD_BACKGROUND);
2306 }
2307 //
2308 // Repaint flag will be used when process CfUpdateHelpString, so restore its value
2309 // if we didn't break halfway when process CfRefreshHighLight.
2310 //
2311 Repaint = SavedValue;
2312 break;
2313
2314 case CfUpdateHelpString:
2315 ControlFlag = CfPrepareToReadKey;
2316 if (Selection->Form->ModalForm) {
2317 break;
2318 }
2319
2320 if (Repaint || NewLine) {
2321 //
2322 // Don't print anything if it is a NULL help token
2323 //
2324 ASSERT(MenuOption != NULL);
2325 if (MenuOption->ThisTag->Help == 0 || !IsSelectable (MenuOption)) {
2326 StringPtr = L"\0";
2327 } else {
2328 StringPtr = GetToken (MenuOption->ThisTag->Help, MenuOption->Handle);
2329 }
2330
2331 ProcessHelpString (StringPtr, &FormattedString, BottomRow - TopRow);
2332
2333 gST->ConOut->SetAttribute (gST->ConOut, HELP_TEXT | FIELD_BACKGROUND);
2334
2335 for (Index = 0; Index < BottomRow - TopRow; Index++) {
2336 //
2337 // Pad String with spaces to simulate a clearing of the previous line
2338 //
2339 for (; GetStringWidth (&FormattedString[Index * gHelpBlockWidth * 2]) / 2 < gHelpBlockWidth;) {
2340 StrCat (&FormattedString[Index * gHelpBlockWidth * 2], L" ");
2341 }
2342
2343 PrintStringAt (
2344 LocalScreen.RightColumn - gHelpBlockWidth,
2345 Index + TopRow,
2346 &FormattedString[Index * gHelpBlockWidth * 2]
2347 );
2348 }
2349 }
2350 //
2351 // Reset this flag every time we finish using it.
2352 //
2353 Repaint = FALSE;
2354 NewLine = FALSE;
2355 break;
2356
2357 case CfPrepareToReadKey:
2358 ControlFlag = CfReadKey;
2359 ScreenOperation = UiNoOperation;
2360 break;
2361
2362 case CfReadKey:
2363 ControlFlag = CfScreenOperation;
2364
2365 //
2366 // Wait for user's selection
2367 //
2368 do {
2369 Status = UiWaitForSingleEvent (gST->ConIn->WaitForKey, 0, MinRefreshInterval);
2370 } while (Status == EFI_TIMEOUT);
2371
2372 if (Selection->Action == UI_ACTION_REFRESH_FORMSET) {
2373 //
2374 // IFR is updated in Callback of refresh opcode, re-parse it
2375 //
2376 Selection->Statement = NULL;
2377 return EFI_SUCCESS;
2378 }
2379
2380 Status = gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
2381 //
2382 // If we encounter error, continue to read another key in.
2383 //
2384 if (EFI_ERROR (Status)) {
2385 ControlFlag = CfReadKey;
2386 break;
2387 }
2388
2389 switch (Key.UnicodeChar) {
2390 case CHAR_CARRIAGE_RETURN:
2391 ScreenOperation = UiSelect;
2392 gDirection = 0;
2393 break;
2394
2395 //
2396 // We will push the adjustment of these numeric values directly to the input handler
2397 // NOTE: we won't handle manual input numeric
2398 //
2399 case '+':
2400 case '-':
2401 //
2402 // If the screen has no menu items, and the user didn't select UiReset
2403 // ignore the selection and go back to reading keys.
2404 //
2405 if(IsListEmpty (&gMenuOption)) {
2406 ControlFlag = CfReadKey;
2407 break;
2408 }
2409
2410 ASSERT(MenuOption != NULL);
2411 Statement = MenuOption->ThisTag;
2412 if ((Statement->Operand == EFI_IFR_DATE_OP)
2413 || (Statement->Operand == EFI_IFR_TIME_OP)
2414 || ((Statement->Operand == EFI_IFR_NUMERIC_OP) && (Statement->Step != 0))
2415 ){
2416 if (Key.UnicodeChar == '+') {
2417 gDirection = SCAN_RIGHT;
2418 } else {
2419 gDirection = SCAN_LEFT;
2420 }
2421 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2422 if (EFI_ERROR (Status)) {
2423 //
2424 // Repaint to clear possible error prompt pop-up
2425 //
2426 Repaint = TRUE;
2427 NewLine = TRUE;
2428 } else {
2429 Selection->Action = UI_ACTION_REFRESH_FORM;
2430 }
2431 if (OptionString != NULL) {
2432 FreePool (OptionString);
2433 }
2434 }
2435 break;
2436
2437 case '^':
2438 ScreenOperation = UiUp;
2439 break;
2440
2441 case 'V':
2442 case 'v':
2443 ScreenOperation = UiDown;
2444 break;
2445
2446 case ' ':
2447 if ((gClassOfVfr & FORMSET_CLASS_FRONT_PAGE) != FORMSET_CLASS_FRONT_PAGE) {
2448 //
2449 // If the screen has no menu items, and the user didn't select UiReset
2450 // ignore the selection and go back to reading keys.
2451 //
2452 if(IsListEmpty (&gMenuOption)) {
2453 ControlFlag = CfReadKey;
2454 break;
2455 }
2456
2457 ASSERT(MenuOption != NULL);
2458 if (MenuOption->ThisTag->Operand == EFI_IFR_CHECKBOX_OP && !MenuOption->GrayOut) {
2459 ScreenOperation = UiSelect;
2460 }
2461 }
2462 break;
2463
2464 case CHAR_NULL:
2465 if (((Key.ScanCode == SCAN_F9) && ((gFunctionKeySetting & FUNCTION_NINE) != FUNCTION_NINE)) ||
2466 ((Key.ScanCode == SCAN_F10) && ((gFunctionKeySetting & FUNCTION_TEN) != FUNCTION_TEN))
2467 ) {
2468 //
2469 // If the function key has been disabled, just ignore the key.
2470 //
2471 } else {
2472 for (Index = 0; Index < sizeof (gScanCodeToOperation) / sizeof (gScanCodeToOperation[0]); Index++) {
2473 if (Selection->Form->ModalForm &&
2474 (Key.ScanCode == SCAN_F9 || Key.ScanCode == SCAN_F10 || Key.ScanCode == SCAN_ESC)) {
2475 ControlFlag = CfReadKey;
2476 break;
2477 }
2478
2479 if (Key.ScanCode == gScanCodeToOperation[Index].ScanCode) {
2480 if (Key.ScanCode == SCAN_F9) {
2481 //
2482 // Reset to standard default
2483 //
2484 DefaultId = EFI_HII_DEFAULT_CLASS_STANDARD;
2485 }
2486 ScreenOperation = gScanCodeToOperation[Index].ScreenOperation;
2487 break;
2488 }
2489 }
2490 }
2491 break;
2492 }
2493 break;
2494
2495 case CfScreenOperation:
2496 if (ScreenOperation != UiReset) {
2497 //
2498 // If the screen has no menu items, and the user didn't select UiReset
2499 // ignore the selection and go back to reading keys.
2500 //
2501 if (IsListEmpty (&gMenuOption)) {
2502 ControlFlag = CfReadKey;
2503 break;
2504 }
2505 }
2506
2507 for (Index = 0;
2508 Index < sizeof (gScreenOperationToControlFlag) / sizeof (gScreenOperationToControlFlag[0]);
2509 Index++
2510 ) {
2511 if (ScreenOperation == gScreenOperationToControlFlag[Index].ScreenOperation) {
2512 ControlFlag = gScreenOperationToControlFlag[Index].ControlFlag;
2513 break;
2514 }
2515 }
2516 break;
2517
2518 case CfUiSelect:
2519 ControlFlag = CfCheckSelection;
2520
2521 ASSERT(MenuOption != NULL);
2522 Statement = MenuOption->ThisTag;
2523 if (Statement->Operand == EFI_IFR_TEXT_OP) {
2524 break;
2525 }
2526
2527 //
2528 // Keep highlight on current MenuOption
2529 //
2530 Selection->QuestionId = Statement->QuestionId;
2531
2532 switch (Statement->Operand) {
2533 case EFI_IFR_REF_OP:
2534 if (Statement->RefDevicePath != 0) {
2535 if (Selection->Form->ModalForm) {
2536 break;
2537 }
2538 //
2539 // Goto another Hii Package list
2540 //
2541 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2542
2543 StringPtr = GetToken (Statement->RefDevicePath, Selection->FormSet->HiiHandle);
2544 if (StringPtr == NULL) {
2545 //
2546 // No device path string not found, exit
2547 //
2548 Selection->Action = UI_ACTION_EXIT;
2549 Selection->Statement = NULL;
2550 break;
2551 }
2552 BufferSize = StrLen (StringPtr) / 2;
2553 DevicePath = AllocatePool (BufferSize);
2554 ASSERT (DevicePath != NULL);
2555
2556 //
2557 // Convert from Device Path String to DevicePath Buffer in the reverse order.
2558 //
2559 DevicePathBuffer = (UINT8 *) DevicePath;
2560 for (Index = 0; StringPtr[Index] != L'\0'; Index ++) {
2561 TemStr[0] = StringPtr[Index];
2562 DigitUint8 = (UINT8) StrHexToUint64 (TemStr);
2563 if (DigitUint8 == 0 && TemStr[0] != L'0') {
2564 //
2565 // Invalid Hex Char as the tail.
2566 //
2567 break;
2568 }
2569 if ((Index & 1) == 0) {
2570 DevicePathBuffer [Index/2] = DigitUint8;
2571 } else {
2572 DevicePathBuffer [Index/2] = (UINT8) ((DevicePathBuffer [Index/2] << 4) + DigitUint8);
2573 }
2574 }
2575
2576 Selection->Handle = DevicePathToHiiHandle (DevicePath);
2577 if (Selection->Handle == NULL) {
2578 //
2579 // If target Hii Handle not found, exit
2580 //
2581 Selection->Action = UI_ACTION_EXIT;
2582 Selection->Statement = NULL;
2583 break;
2584 }
2585
2586 FreePool (StringPtr);
2587 FreePool (DevicePath);
2588
2589 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2590 Selection->FormId = Statement->RefFormId;
2591 Selection->QuestionId = Statement->RefQuestionId;
2592 } else if (!CompareGuid (&Statement->RefFormSetId, &gZeroGuid)) {
2593 if (Selection->Form->ModalForm) {
2594 break;
2595 }
2596 //
2597 // Goto another Formset, check for uncommitted data
2598 //
2599 Selection->Action = UI_ACTION_REFRESH_FORMSET;
2600
2601 CopyMem (&Selection->FormSetGuid, &Statement->RefFormSetId, sizeof (EFI_GUID));
2602 Selection->FormId = Statement->RefFormId;
2603 Selection->QuestionId = Statement->RefQuestionId;
2604 } else if (Statement->RefFormId != 0) {
2605 //
2606 // Check whether target From is suppressed.
2607 //
2608 RefForm = IdToForm (Selection->FormSet, Statement->RefFormId);
2609
2610 if ((RefForm != NULL) && (RefForm->SuppressExpression != NULL)) {
2611 Status = EvaluateExpression (Selection->FormSet, RefForm, RefForm->SuppressExpression);
2612 if (EFI_ERROR (Status)) {
2613 return Status;
2614 }
2615
2616 if (RefForm->SuppressExpression->Result.Value.b) {
2617 //
2618 // Form is suppressed.
2619 //
2620 do {
2621 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gFormSuppress, gPressEnter, gEmptyString);
2622 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
2623
2624 Repaint = TRUE;
2625 break;
2626 }
2627 }
2628
2629 //
2630 // Goto another form inside this formset,
2631 //
2632 Selection->Action = UI_ACTION_REFRESH_FORM;
2633
2634 //
2635 // Link current form so that we can always go back when someone hits the ESC
2636 //
2637 MenuList = UiFindMenuList (&Selection->FormSetGuid, Statement->RefFormId);
2638 if (MenuList == NULL) {
2639 MenuList = UiAddMenuList (CurrentMenu, &Selection->FormSetGuid, Statement->RefFormId);
2640 }
2641
2642 Selection->FormId = Statement->RefFormId;
2643 Selection->QuestionId = Statement->RefQuestionId;
2644 } else if (Statement->RefQuestionId != 0) {
2645 //
2646 // Goto another Question
2647 //
2648 Selection->QuestionId = Statement->RefQuestionId;
2649
2650 if ((Statement->QuestionFlags & EFI_IFR_FLAG_CALLBACK) != 0) {
2651 Selection->Action = UI_ACTION_REFRESH_FORM;
2652 } else {
2653 Repaint = TRUE;
2654 NewLine = TRUE;
2655 break;
2656 }
2657 }
2658 break;
2659
2660 case EFI_IFR_ACTION_OP:
2661 //
2662 // Process the Config string <ConfigResp>
2663 //
2664 Status = ProcessQuestionConfig (Selection, Statement);
2665
2666 if (EFI_ERROR (Status)) {
2667 break;
2668 }
2669
2670 //
2671 // The action button may change some Question value, so refresh the form
2672 //
2673 Selection->Action = UI_ACTION_REFRESH_FORM;
2674 break;
2675
2676 case EFI_IFR_RESET_BUTTON_OP:
2677 //
2678 // Reset Question to default value specified by DefaultId
2679 //
2680 ControlFlag = CfUiDefault;
2681 DefaultId = Statement->DefaultId;
2682 break;
2683
2684 default:
2685 //
2686 // Editable Questions: oneof, ordered list, checkbox, numeric, string, password
2687 //
2688 UpdateKeyHelp (Selection, MenuOption, TRUE);
2689 Status = ProcessOptions (Selection, MenuOption, TRUE, &OptionString);
2690
2691 if (EFI_ERROR (Status)) {
2692 Repaint = TRUE;
2693 NewLine = TRUE;
2694 UpdateKeyHelp (Selection, MenuOption, FALSE);
2695 } else {
2696 Selection->Action = UI_ACTION_REFRESH_FORM;
2697 }
2698
2699 if (OptionString != NULL) {
2700 FreePool (OptionString);
2701 }
2702 break;
2703 }
2704 break;
2705
2706 case CfUiReset:
2707 //
2708 // We come here when someone press ESC
2709 //
2710 ControlFlag = CfCheckSelection;
2711 if (FindNextMenu (Selection, &Repaint, &NewLine)) {
2712 return EFI_SUCCESS;
2713 }
2714 break;
2715
2716 case CfUiLeft:
2717 ControlFlag = CfCheckSelection;
2718 ASSERT(MenuOption != NULL);
2719 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2720 if (MenuOption->Sequence != 0) {
2721 //
2722 // In the middle or tail of the Date/Time op-code set, go left.
2723 //
2724 ASSERT(NewPos != NULL);
2725 NewPos = NewPos->BackLink;
2726 }
2727 }
2728 break;
2729
2730 case CfUiRight:
2731 ControlFlag = CfCheckSelection;
2732 ASSERT(MenuOption != NULL);
2733 if ((MenuOption->ThisTag->Operand == EFI_IFR_DATE_OP) || (MenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)) {
2734 if (MenuOption->Sequence != 2) {
2735 //
2736 // In the middle or tail of the Date/Time op-code set, go left.
2737 //
2738 ASSERT(NewPos != NULL);
2739 NewPos = NewPos->ForwardLink;
2740 }
2741 }
2742 break;
2743
2744 case CfUiUp:
2745 ControlFlag = CfCheckSelection;
2746
2747 SavedListEntry = NewPos;
2748
2749 ASSERT(NewPos != NULL);
2750 //
2751 // Adjust Date/Time position before we advance forward.
2752 //
2753 AdjustDateAndTimePosition (TRUE, &NewPos);
2754 if (NewPos->BackLink != &gMenuOption) {
2755 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2756 NewLine = TRUE;
2757 NewPos = NewPos->BackLink;
2758
2759 PreviousMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2760 DistanceValue = PreviousMenuOption->Skip;
2761 Difference = 0;
2762 if (MenuOption->Row >= DistanceValue + TopRow) {
2763 Difference = MoveToNextStatement (TRUE, &NewPos, MenuOption->Row - TopRow - DistanceValue);
2764 }
2765 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
2766
2767 ASSERT (MenuOption != NULL);
2768 if (Difference < 0) {
2769 //
2770 // We hit the begining MenuOption that can be focused
2771 // so we simply scroll to the top.
2772 //
2773 if (TopOfScreen != gMenuOption.ForwardLink) {
2774 TopOfScreen = gMenuOption.ForwardLink;
2775 Repaint = TRUE;
2776 } else {
2777 //
2778 // Scroll up to the last page when we have arrived at top page.
2779 //
2780 NewPos = &gMenuOption;
2781 TopOfScreen = &gMenuOption;
2782 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2783 ScreenOperation = UiPageUp;
2784 ControlFlag = CfScreenOperation;
2785 break;
2786 }
2787 } else if (MenuOption->Row < TopRow + DistanceValue + Difference) {
2788 //
2789 // Previous focus MenuOption is above the TopOfScreen, so we need to scroll
2790 //
2791 TopOfScreen = NewPos;
2792 Repaint = TRUE;
2793 SkipValue = 0;
2794 OldSkipValue = 0;
2795 } else if (!IsSelectable (NextMenuOption)) {
2796 //
2797 // Continue to go up until scroll to next page or the selectable option is found.
2798 //
2799 ScreenOperation = UiUp;
2800 ControlFlag = CfScreenOperation;
2801 }
2802
2803 //
2804 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2805 //
2806 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2807 AdjustDateAndTimePosition (TRUE, &NewPos);
2808 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2809 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
2810 } else {
2811 //
2812 // Scroll up to the last page.
2813 //
2814 NewPos = &gMenuOption;
2815 TopOfScreen = &gMenuOption;
2816 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2817 ScreenOperation = UiPageUp;
2818 ControlFlag = CfScreenOperation;
2819 }
2820 break;
2821
2822 case CfUiPageUp:
2823 ControlFlag = CfCheckSelection;
2824
2825 ASSERT(NewPos != NULL);
2826 if (NewPos->BackLink == &gMenuOption) {
2827 NewLine = FALSE;
2828 Repaint = FALSE;
2829 break;
2830 }
2831
2832 NewLine = TRUE;
2833 Repaint = TRUE;
2834 Link = TopOfScreen;
2835 Index = BottomRow;
2836 while ((Index >= TopRow) && (Link->BackLink != &gMenuOption)) {
2837 Link = Link->BackLink;
2838 PreviousMenuOption = MENU_OPTION_FROM_LINK (Link);
2839 if (Index < PreviousMenuOption->Skip) {
2840 Index = 0;
2841 break;
2842 }
2843 Index = Index - PreviousMenuOption->Skip;
2844 }
2845
2846 if ((Link->BackLink == &gMenuOption) && (Index >= TopRow)) {
2847 if (TopOfScreen == &gMenuOption) {
2848 TopOfScreen = gMenuOption.ForwardLink;
2849 NewPos = gMenuOption.BackLink;
2850 MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow);
2851 Repaint = FALSE;
2852 } else if (TopOfScreen != Link) {
2853 TopOfScreen = Link;
2854 NewPos = Link;
2855 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
2856 } else {
2857 //
2858 // Finally we know that NewPos is the last MenuOption can be focused.
2859 //
2860 Repaint = FALSE;
2861 NewPos = Link;
2862 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
2863 }
2864 } else {
2865 if (Index + 1 < TopRow) {
2866 //
2867 // Back up the previous option.
2868 //
2869 Link = Link->ForwardLink;
2870 }
2871
2872 //
2873 // Move to the option in Next page.
2874 //
2875 if (TopOfScreen == &gMenuOption) {
2876 NewPos = gMenuOption.BackLink;
2877 MoveToNextStatement (TRUE, &NewPos, BottomRow - TopRow);
2878 } else {
2879 NewPos = Link;
2880 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
2881 }
2882
2883 //
2884 // There are more MenuOption needing scrolling up.
2885 //
2886 TopOfScreen = Link;
2887 MenuOption = NULL;
2888 }
2889
2890 //
2891 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2892 // Don't do this when we are already in the first page.
2893 //
2894 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2895 AdjustDateAndTimePosition (TRUE, &NewPos);
2896 break;
2897
2898 case CfUiPageDown:
2899 ControlFlag = CfCheckSelection;
2900
2901 ASSERT (NewPos != NULL);
2902 if (NewPos->ForwardLink == &gMenuOption) {
2903 NewLine = FALSE;
2904 Repaint = FALSE;
2905 break;
2906 }
2907
2908 NewLine = TRUE;
2909 Repaint = TRUE;
2910 Link = TopOfScreen;
2911 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2912 Index = TopRow;
2913 while ((Index <= BottomRow) && (Link->ForwardLink != &gMenuOption)) {
2914 Index = Index + NextMenuOption->Skip;
2915 Link = Link->ForwardLink;
2916 NextMenuOption = MENU_OPTION_FROM_LINK (Link);
2917 }
2918
2919 if ((Link->ForwardLink == &gMenuOption) && (Index <= BottomRow)) {
2920 //
2921 // Finally we know that NewPos is the last MenuOption can be focused.
2922 //
2923 Repaint = FALSE;
2924 MoveToNextStatement (TRUE, &Link, Index - TopRow);
2925 } else {
2926 if (Index - 1 > BottomRow) {
2927 //
2928 // Back up the previous option.
2929 //
2930 Link = Link->BackLink;
2931 }
2932 //
2933 // There are more MenuOption needing scrolling down.
2934 //
2935 TopOfScreen = Link;
2936 MenuOption = NULL;
2937 //
2938 // Move to the option in Next page.
2939 //
2940 MoveToNextStatement (FALSE, &Link, BottomRow - TopRow);
2941 }
2942
2943 //
2944 // If we encounter a Date/Time op-code set, rewind to the first op-code of the set.
2945 // Don't do this when we are already in the last page.
2946 //
2947 NewPos = Link;
2948 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2949 AdjustDateAndTimePosition (TRUE, &NewPos);
2950 break;
2951
2952 case CfUiDown:
2953 ControlFlag = CfCheckSelection;
2954 //
2955 // Since the behavior of hitting the down arrow on a Date/Time op-code is intended
2956 // to be one that progresses to the next set of op-codes, we need to advance to the last
2957 // Date/Time op-code and leave the remaining logic in UiDown intact so the appropriate
2958 // checking can be done. The only other logic we need to introduce is that if a Date/Time
2959 // op-code is the last entry in the menu, we need to rewind back to the first op-code of
2960 // the Date/Time op-code.
2961 //
2962 SavedListEntry = NewPos;
2963 AdjustDateAndTimePosition (FALSE, &NewPos);
2964
2965 if (NewPos->ForwardLink != &gMenuOption) {
2966 MenuOption = MENU_OPTION_FROM_LINK (NewPos);
2967 NewLine = TRUE;
2968 NewPos = NewPos->ForwardLink;
2969
2970 Difference = 0;
2971 if (BottomRow >= MenuOption->Row + MenuOption->Skip) {
2972 Difference = MoveToNextStatement (FALSE, &NewPos, BottomRow - MenuOption->Row - MenuOption->Skip);
2973 //
2974 // We hit the end of MenuOption that can be focused
2975 // so we simply scroll to the first page.
2976 //
2977 if (Difference < 0) {
2978 //
2979 // Scroll to the first page.
2980 //
2981 if (TopOfScreen != gMenuOption.ForwardLink) {
2982 TopOfScreen = gMenuOption.ForwardLink;
2983 Repaint = TRUE;
2984 MenuOption = NULL;
2985 } else {
2986 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
2987 }
2988 NewPos = gMenuOption.ForwardLink;
2989 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
2990
2991 //
2992 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
2993 //
2994 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
2995 AdjustDateAndTimePosition (TRUE, &NewPos);
2996 break;
2997 }
2998 }
2999 NextMenuOption = MENU_OPTION_FROM_LINK (NewPos);
3000
3001 //
3002 // An option might be multi-line, so we need to reflect that data in the overall skip value
3003 //
3004 UpdateOptionSkipLines (Selection, NextMenuOption, &OptionString, (UINTN) SkipValue);
3005 DistanceValue = Difference + NextMenuOption->Skip;
3006
3007 Temp = MenuOption->Row + MenuOption->Skip + DistanceValue - 1;
3008 if ((MenuOption->Row + MenuOption->Skip == BottomRow + 1) &&
3009 (NextMenuOption->ThisTag->Operand == EFI_IFR_DATE_OP ||
3010 NextMenuOption->ThisTag->Operand == EFI_IFR_TIME_OP)
3011 ) {
3012 Temp ++;
3013 }
3014
3015 //
3016 // If we are going to scroll, update TopOfScreen
3017 //
3018 if (Temp > BottomRow) {
3019 do {
3020 //
3021 // Is the current top of screen a zero-advance op-code?
3022 // If so, keep moving forward till we hit a >0 advance op-code
3023 //
3024 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3025
3026 //
3027 // If bottom op-code is more than one line or top op-code is more than one line
3028 //
3029 if ((DistanceValue > 1) || (MenuOption->Skip > 1)) {
3030 //
3031 // Is the bottom op-code greater than or equal in size to the top op-code?
3032 //
3033 if ((Temp - BottomRow) >= (SavedMenuOption->Skip - OldSkipValue)) {
3034 //
3035 // Skip the top op-code
3036 //
3037 TopOfScreen = TopOfScreen->ForwardLink;
3038 Difference = (Temp - BottomRow) - (SavedMenuOption->Skip - OldSkipValue);
3039
3040 OldSkipValue = Difference;
3041
3042 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3043
3044 //
3045 // If we have a remainder, skip that many more op-codes until we drain the remainder
3046 //
3047 while (Difference >= (INTN) SavedMenuOption->Skip) {
3048 //
3049 // Since the Difference is greater than or equal to this op-code's skip value, skip it
3050 //
3051 Difference = Difference - (INTN) SavedMenuOption->Skip;
3052 TopOfScreen = TopOfScreen->ForwardLink;
3053 SavedMenuOption = MENU_OPTION_FROM_LINK (TopOfScreen);
3054 }
3055 //
3056 // Since we will act on this op-code in the next routine, and increment the
3057 // SkipValue, set the skips to one less than what is required.
3058 //
3059 SkipValue = Difference - 1;
3060
3061 } else {
3062 //
3063 // Since we will act on this op-code in the next routine, and increment the
3064 // SkipValue, set the skips to one less than what is required.
3065 //
3066 SkipValue = OldSkipValue + (Temp - BottomRow) - 1;
3067 }
3068 } else {
3069 if ((OldSkipValue + 1) == (INTN) SavedMenuOption->Skip) {
3070 TopOfScreen = TopOfScreen->ForwardLink;
3071 break;
3072 } else {
3073 SkipValue = OldSkipValue;
3074 }
3075 }
3076 //
3077 // If the op-code at the top of the screen is more than one line, let's not skip it yet
3078 // Let's set a skip flag to smoothly scroll the top of the screen.
3079 //
3080 if (SavedMenuOption->Skip > 1) {
3081 if (SavedMenuOption == NextMenuOption) {
3082 SkipValue = 0;
3083 } else {
3084 SkipValue++;
3085 }
3086 } else if (SavedMenuOption->Skip == 1) {
3087 SkipValue = 0;
3088 } else {
3089 SkipValue = 0;
3090 TopOfScreen = TopOfScreen->ForwardLink;
3091 }
3092 } while (SavedMenuOption->Skip == 0);
3093
3094 Repaint = TRUE;
3095 OldSkipValue = SkipValue;
3096 } else if (!IsSelectable (NextMenuOption)) {
3097 //
3098 // Continue to go down until scroll to next page or the selectable option is found.
3099 //
3100 ScreenOperation = UiDown;
3101 ControlFlag = CfScreenOperation;
3102 }
3103
3104 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3105
3106 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3107
3108 } else {
3109 //
3110 // Scroll to the first page.
3111 //
3112 if (TopOfScreen != gMenuOption.ForwardLink) {
3113 TopOfScreen = gMenuOption.ForwardLink;
3114 Repaint = TRUE;
3115 MenuOption = NULL;
3116 } else {
3117 MenuOption = MENU_OPTION_FROM_LINK (SavedListEntry);
3118 }
3119 NewLine = TRUE;
3120 NewPos = gMenuOption.ForwardLink;
3121 MoveToNextStatement (FALSE, &NewPos, BottomRow - TopRow);
3122 }
3123
3124 //
3125 // If we are at the end of the list and sitting on a Date/Time op, rewind to the head.
3126 //
3127 AdjustDateAndTimePosition (TRUE, &TopOfScreen);
3128 AdjustDateAndTimePosition (TRUE, &NewPos);
3129 break;
3130
3131 case CfUiSave:
3132 ControlFlag = CfCheckSelection;
3133
3134 //
3135 // Submit the form
3136 //
3137 Status = SubmitForm (Selection->FormSet, Selection->Form, FALSE);
3138
3139 if (!EFI_ERROR (Status)) {
3140 ASSERT(MenuOption != NULL);
3141 UpdateStatusBar (Selection, INPUT_ERROR, MenuOption->ThisTag->QuestionFlags, FALSE);
3142 UpdateStatusBar (Selection, NV_UPDATE_REQUIRED, MenuOption->ThisTag->QuestionFlags, FALSE);
3143 } else {
3144 do {
3145 CreateDialog (4, TRUE, 0, NULL, &Key, gEmptyString, gSaveFailed, gPressEnter, gEmptyString);
3146 } while (Key.UnicodeChar != CHAR_CARRIAGE_RETURN);
3147
3148 Repaint = TRUE;
3149 NewLine = TRUE;
3150 }
3151 break;
3152
3153 case CfUiDefault:
3154 ControlFlag = CfCheckSelection;
3155 if (!Selection->FormEditable) {
3156 //
3157 // This Form is not editable, ignore the F9 (reset to default)
3158 //
3159 break;
3160 }
3161
3162 Status = ExtractFormDefault (Selection->FormSet, Selection->Form, DefaultId);
3163
3164 if (!EFI_ERROR (Status)) {
3165 Selection->Action = UI_ACTION_REFRESH_FORM;
3166 Selection->Statement = NULL;
3167
3168 //
3169 // Show NV update flag on status bar
3170 //
3171 UpdateNvInfoInForm(Selection->FormSet, TRUE);
3172 gResetRequired = TRUE;
3173 }
3174 break;
3175
3176 case CfUiNoOperation:
3177 ControlFlag = CfCheckSelection;
3178 break;
3179
3180 case CfExit:
3181 UiFreeRefreshList ();
3182
3183 gST->ConOut->SetAttribute (gST->ConOut, EFI_TEXT_ATTR (EFI_LIGHTGRAY, EFI_BLACK));
3184 gST->ConOut->SetCursorPosition (gST->ConOut, 0, Row + 4);
3185 gST->ConOut->EnableCursor (gST->ConOut, TRUE);
3186 gST->ConOut->OutputString (gST->ConOut, L"\n");
3187
3188 return EFI_SUCCESS;
3189
3190 default:
3191 break;
3192 }
3193 }
3194 }