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