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