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