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