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