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